<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss/styles.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Antoine Lehurt</title><description>Product engineer based in Stockholm. Writing about how I work, what I&apos;m figuring out, and the tools behind it.</description><link>https://kewah.com/</link><item><title>What cheap code solves</title><link>https://kewah.com/what-cheap-code-solves/</link><guid isPermaLink="true">https://kewah.com/what-cheap-code-solves/</guid><description>Cheap code reshapes what&apos;s worth building, but not equally. Personal tools and internal layers thrive because the hard problems are already solved. Customer-facing products still answer to distribution, trust, and compounding complexity.
</description><pubDate>Sat, 11 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A few months ago I built a coffee tracker. Not because I desperately needed one. I&apos;d been using an off-the-shelf app and it was fine. It did the job. It just wasn&apos;t tuned for how I actually use it, and it came with a pile of features I never touched.&lt;/p&gt;
&lt;p&gt;This time I could build exactly what I wanted. A few hours with an AI agent, and the thing worked the way I&apos;d always wanted it to. Two years ago this project dies on the second weekend. The gap between what&apos;s in my head and what&apos;s on screen is still too wide, and the motivation is already gone.&lt;/p&gt;
&lt;p&gt;I have a graveyard of those.&lt;/p&gt;
&lt;p&gt;AI coding agents collapsed the friction that used to kill that kind of software. I&apos;ve been rebuilding a small ecosystem of tools around how I actually work, capture, reading, tracking.&lt;/p&gt;
&lt;p&gt;Personal software is where the shift feels most obvious. I already know the user and the workflow. I don&apos;t need research, alignment, or buy-in. I just need to turn a private annoyance into a working tool before I lose interest. That&apos;s why more of these things become worth making.&lt;/p&gt;
&lt;p&gt;Internal software is different. Cheap code helps there too, but mostly when the hard parts are already settled. The users are known. The workflow is visible. The data already exists, usually scattered across a few systems and one cursed spreadsheet. The work is in the last mile, building the layer that makes an existing workflow less painful.&lt;/p&gt;
&lt;p&gt;I&apos;ve seen that at work. Support teams have used Lovable to build tools on top of data they already had access to. Engineering still mattered, and so did the underlying constraints. What got cheaper was the last mile, the workflow layer that turned existing data into something actually useful. It also meant they didn&apos;t need to wait for engineering to prioritize that exact layer for them.&lt;/p&gt;
&lt;p&gt;That is real leverage. It works because the coordination problem is already mostly solved.&lt;/p&gt;
&lt;p&gt;Cheap code helps most where usefulness can outrun rough edges. A personal tool can be messy and still earn its keep. A workflow layer inside a team can too. Customer-facing products are different.&lt;/p&gt;
&lt;p&gt;This is where the cheap-code story starts to thin out. You still need a problem enough people care about, and you still need them to switch and stay.&lt;/p&gt;
&lt;p&gt;Cheap code helps most once you already have a hypothesis. It makes exploration cheaper, and it makes early validation easier because you can put a rough version in front of people fast. You can learn with something real instead of a doc or a mockup. But the later questions still wait for you: adoption, trust, and long-term complexity.&lt;/p&gt;
&lt;p&gt;That&apos;s why &lt;a href=&quot;https://www.joanwestenberg.com/the-hacker-news-tarpit/&quot;&gt;The Hacker News tarpit&lt;/a&gt; lands so hard. JA Westenberg cloned Hacker News in a few hours. On the surface it looks simple: links, votes, comments, accounts. But nobody is going to use that clone, because Hacker News is not mainly a bundle of features. It&apos;s audience, norms, moderation, status, and habit layered on top of ordinary software.&lt;/p&gt;
&lt;p&gt;Hacker News is an extreme case. The mechanism isn&apos;t.&lt;/p&gt;
&lt;p&gt;When you&apos;re building for strangers, the hard part is getting into somebody else&apos;s life. You have to reach people, earn enough trust for them to try the thing, and give them a reason to change habits they already have.&lt;/p&gt;
&lt;p&gt;That also changes the kind of discipline software demands. When adding something gets cheap, bloat becomes the default failure mode. Features pile up because they&apos;re easy to ship, not because they make the product sharper. Maintenance may get cheaper too, at least for a while. Complexity doesn&apos;t. It compounds.&lt;/p&gt;
&lt;p&gt;And compounding complexity is exactly where both &lt;a href=&quot;/the-map-coding-left-behing/&quot;&gt;humans and agents start to lose the map&lt;/a&gt;. Shipping gets easier faster than understanding does. Humans were never great at holding a full model of a growing system in their heads, and I don&apos;t see much reason to believe agents have solved that problem either. If anything, they make it easier to defer the understanding until the system is already harder to reason about.&lt;/p&gt;
&lt;p&gt;Maybe that&apos;s the real change. Cheap code doesn&apos;t make software simple. It makes more software worth attempting.&lt;/p&gt;
&lt;p&gt;Personal tools, rough prototypes, and narrow internal layers can survive with rough edges because usefulness outruns polish. Customer-facing products live under a different gravity. Trust, habit, coordination, and complexity catch up fast.&lt;/p&gt;
&lt;p&gt;Getting to a working thing is cheaper now. The harder part begins when other people have to trust it, use it, and keep depending on it.&lt;/p&gt;
</content:encoded></item><item><title>The craft was never the typing</title><link>https://kewah.com/the-craft-was-never-the-typing/</link><guid isPermaLink="true">https://kewah.com/the-craft-was-never-the-typing/</guid><description>The typing changed hands. The craft didn&apos;t.
</description><pubDate>Mon, 06 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve been chasing better code since I started programming. Every review, every refactor, every late realization that last month&apos;s approach was wrong, that was the craft. An infinite loop of learning, and I was always hungry for the next iteration.&lt;/p&gt;
&lt;p&gt;Then an agent started writing code that looked a lot like mine. At first, it wasn&apos;t good enough. The output was close but not something I&apos;d ship, and I spent more time fixing than I would have writing it myself.&lt;/p&gt;
&lt;p&gt;But the models got better. Opus 4 was a real jump. I started delegating the coding part entirely, and for a while it felt like I&apos;d just gotten faster. Then the review cycles started piling up. Back and forth, back and forth. The agent wasn&apos;t wrong exactly, it just wasn&apos;t making the choices I would have made, because I hadn&apos;t told it to.&lt;/p&gt;
&lt;p&gt;That&apos;s when the work shifted. I started planning more deliberately than I ever had when the code came from my own hands, spelling out architecture and tradeoffs and exactly how something should feel to use. Decisions I used to make silently while typing, from instinct. The agent can&apos;t work from instinct. It needs that precision. And putting it down, finding the words for what I used to just know, that was a skill I&apos;d never needed before.&lt;/p&gt;
&lt;p&gt;I was never just writing software. Nobody using it cares who typed it.&lt;/p&gt;
&lt;p&gt;I was building something for a person, inside the constraints of a business, and the code was one tool I reached for among several. A tool I still care about (I&apos;d be lying if I said otherwise), but never the whole job.&lt;/p&gt;
&lt;p&gt;I&apos;m still chasing better code. I just don&apos;t type it anymore.&lt;/p&gt;
</content:encoded></item><item><title>The map coding left behind</title><link>https://kewah.com/the-map-coding-left-behind/</link><guid isPermaLink="true">https://kewah.com/the-map-coding-left-behind/</guid><description>Coding built a mental map of every system I worked on. A year of AI agents taught me the map doesn&apos;t come free.
</description><pubDate>Mon, 06 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A year ago, I started delegating most of my coding to AI agents. The longer I worked that way, the less confident I felt in what I shipped.&lt;/p&gt;
&lt;p&gt;The features I&apos;d built by hand years ago, I could still trace end to end: the data flows, the edge cases, why we&apos;d picked one approach over another. But anything new, anything where the agent did the building and I did the reviewing, felt different. I&apos;d planned thoroughly, researched with the agent, and approved every line. My mental model felt thinner than it used to be.&lt;/p&gt;
&lt;p&gt;I kept asking myself why I trusted what I shipped when I wrote the code myself. It was what coding left behind: a map of how everything connected. The product, the domain, the system I was building inside of.&lt;/p&gt;
&lt;p&gt;For most of my career, coding was how I built that map. Every choice I made while writing code added to it. Fitting a new feature into the existing architecture, keeping things maintainable, leaving the code in better shape than I found it. None of those choices were free. Each one cost attention, and that attention became understanding. I couldn&apos;t shape something I didn&apos;t grasp.&lt;/p&gt;
&lt;p&gt;I&apos;ve always shared the map with others. Every engineer does. The colleague who owns the payments service has their own map of that territory, worn in over months of working in the code. When they tell me how it really behaves (not how the docs say it does), I&apos;m borrowing a piece of theirs to fill a gap in mine.&lt;/p&gt;
&lt;p&gt;AI agents offer something that feels similar. I describe what I want, the agent produces something close to what I would&apos;ve written. But the agent&apos;s map is disposable, built from scratch every session and thrown away at the end. It doesn&apos;t know where the gaps are.&lt;/p&gt;
&lt;p&gt;Some things I can hand off without losing much. The boilerplate, the patterns I&apos;ve written a hundred times. I can read those and know what I&apos;m looking at. But the domain, the business logic, the shape of the system. An agent I don&apos;t fully trust can&apos;t give me that.&lt;/p&gt;
&lt;p&gt;I used to go deep into the foundations of our systems, beyond the feature I was building, down to the code it sat on top of. That&apos;s how I learned to trust what I shipped.&lt;/p&gt;
&lt;p&gt;Maybe the answer is building the map earlier. I&apos;ve started planning differently with the agent. More than the feature details. The architecture, the boundaries, how everything connects. In unfamiliar territory, I dive into the code myself first. By the time the agent writes code, I know the territory. I still review what it produces, but that&apos;s not where I expect to catch gaps anymore.&lt;/p&gt;
&lt;p&gt;I&apos;m still iterating on the process. But I need the map before I can trust what I&apos;m building.&lt;/p&gt;
</content:encoded></item><item><title>Integrating AI into my workflow</title><link>https://kewah.com/integrating-ai-into-my-workflow/</link><guid isPermaLink="true">https://kewah.com/integrating-ai-into-my-workflow/</guid><description>I joined a company with a Kotlin backend I&apos;d never touched. Instead of reaching for docs, I used GPT to explain, critique, and refactor my code. A rubber duck that talks back.
</description><pubDate>Tue, 22 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Earlier this year I joined &lt;a href=&quot;https://sanalabs.com/&quot;&gt;Sana&lt;/a&gt; and opened the backend codebase. It was written entirely in &lt;a href=&quot;https://kotlinlang.org/&quot;&gt;Kotlin&lt;/a&gt;. I&apos;d never written a line of it. The frontend was TypeScript, which I knew well, but the backend was a different world. The syntax looked close enough to Java (which I&apos;d worked with at Spotify a few years prior) that I could squint and follow the shape of things, but the idioms were foreign. Extension functions, data classes, scope functions like &lt;code&gt;let&lt;/code&gt; and &lt;code&gt;apply&lt;/code&gt; chained in ways I couldn&apos;t parse on first read.&lt;/p&gt;
&lt;p&gt;My instinct was to open the Kotlin docs, find a tutorial, maybe skim a &quot;Kotlin for Java developers&quot; guide. Instead, I pasted a block of code into GPT and typed: &quot;Explain what this does, line by line.&quot;&lt;/p&gt;
&lt;p&gt;It worked.&lt;/p&gt;
&lt;p&gt;Not perfectly. Not every time. But well enough that I kept doing it. I&apos;d write a function, paste it in, and ask: &quot;Is this idiomatic Kotlin, or am I just writing TypeScript with different punctuation?&quot; GPT would come back with suggestions (use &lt;code&gt;also&lt;/code&gt; here, replace this &lt;code&gt;if&lt;/code&gt; chain with a &lt;code&gt;when&lt;/code&gt; expression, this whole block can collapse into a single &lt;code&gt;let&lt;/code&gt; call) and I&apos;d rewrite, paste again, ask again. A feedback loop that felt less like reading documentation and more like pair programming with someone who never gets impatient.&lt;/p&gt;
&lt;p&gt;I&apos;d tried ChatGPT before this. Asked it to generate code, got back something that looked plausible but wasn&apos;t usable. Didn&apos;t see the point over just writing it myself. But learning Kotlin was different. I wasn&apos;t asking GPT to write code for me. I was asking it to explain code I was already looking at, to critique code I&apos;d already written. I had something specific to push against, and that changed everything. I ran it through &lt;a href=&quot;https://www.raycast.com/pro&quot;&gt;Raycast AI&lt;/a&gt; (powered by GPT-4), a tool I already used daily that had just shipped their AI features.&lt;/p&gt;
&lt;p&gt;It&apos;s a rubber duck that talks back.&lt;/p&gt;
&lt;p&gt;I don&apos;t mean that dismissively. Rubber duck debugging works because the act of explaining your problem forces you to think clearly about it. GPT does that, but it also responds, and the response doesn&apos;t have to be brilliant. It just has to be close enough to give you something to push against. You paste an ugly function, it offers a cleaner version, you spot what it missed, and now you understand the code better than if you&apos;d just stared at it.&lt;/p&gt;
&lt;p&gt;Refactoring is where this hits hardest. You&apos;re working in an existing codebase, you find a function that grew ugly over six months of patches, and you need to clean it up without breaking anything. That&apos;s grunt work. The kind of task where having a second pair of eyes (even artificial ones) saves real time. I paste the function, ask for a refactored version, and use the output as a starting point. &quot;Sometimes maybe good, sometimes maybe shit.&quot; When it&apos;s off, I start over, explain what I&apos;m actually trying to do, paste again. GPT has the biggest patience in the world. It doesn&apos;t care if I ask the same question five different ways. It just keeps answering.&lt;/p&gt;
&lt;p&gt;And then there are TypeScript errors. Anyone who&apos;s worked with complex TypeScript generics knows the feeling. The compiler spits out a type error four lines long, nested three levels deep, referencing types you didn&apos;t write. I used to squint at those for minutes, mentally unpacking each layer. Now I paste them into GPT and get back a plain-language explanation of what&apos;s actually mismatched.&lt;/p&gt;
&lt;p&gt;But none of this works if you can&apos;t tell when it&apos;s wrong. GPT hallucinates. It invents plausible Kotlin APIs that don&apos;t exist. It suggests TypeScript patterns that compile but introduce subtle bugs. It rewrites your function and quietly drops an edge case. You need to know enough to catch that, to read the suggestion and feel the thing that&apos;s off before you run it.&lt;/p&gt;
&lt;p&gt;I still write Kotlin every day. I can write decent code now, but I still need a reviewer (a colleague, or increasingly, an AI) to catch the things I miss, to push it from functional to idiomatic. GPT didn&apos;t teach me Kotlin. But it compressed the awkward early phase (the phase where everything takes three times as long because you&apos;re translating in your head) into something shorter and less painful. I&apos;ll take that.&lt;/p&gt;
</content:encoded></item><item><title>Newsletter to RSS feed</title><link>https://kewah.com/newsletter-to-rss-feed/</link><guid isPermaLink="true">https://kewah.com/newsletter-to-rss-feed/</guid><description>Kill the Newsletter allows users to create a unique email address for each newsletter and automatically generates an RSS feed associated with that address. This makes it easy to centralize newsletter subscriptions and receive them via an RSS reader.
</description><pubDate>Fri, 21 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Earlier this year, I shifted &lt;a href=&quot;/back-to-rss/&quot;&gt;back to RSS feeds&lt;/a&gt; for my news consumption. However, one common challenge I encountered was the lack of RSS feeds support from newsletters.&lt;/p&gt;
&lt;p&gt;While some RSS readers, like &lt;a href=&quot;https://www.inoreader.com/blog/2020/02/declutter-your-inbox-subscribe-to-email-newsletters-straight-into-inoreader.html&quot;&gt;Inoreader&lt;/a&gt;, offer the option to receive newsletters within the app, my preferred apps, &lt;a href=&quot;https://reederapp.com/&quot;&gt;Reeder&lt;/a&gt; on MacOS and iPhone, don&apos;t provide that support. Luckily, I discovered a solution called &lt;a href=&quot;https://kill-the-newsletter.com/&quot;&gt;Kill the Newsletter&lt;/a&gt;. This service allows users to create a dedicated email address for each newsletter, automatically generating an RSS feed that can be easily subscribed to. Easy to use and straight to the point. I love it!&lt;/p&gt;
</content:encoded></item><item><title>Managing multiple Git configurations with [includeIf]</title><link>https://kewah.com/managing-multiple-gitconfig-with-includeif/</link><guid isPermaLink="true">https://kewah.com/managing-multiple-gitconfig-with-includeif/</guid><description>Git&apos;s [includeIf] feature makes managing multiple configurations easy when adjusting settings like email address and signing key while working on different projects.
</description><pubDate>Thu, 11 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I often need to juggle between different Git settings depending on the type of project. For instance, I need to use a different email address and signing key when committing. Luckily, Git has a feature called &lt;a href=&quot;https://git-scm.com/docs/git-config#_includes&quot;&gt;[includeIf]&lt;/a&gt; that helps me manage multiple Git configurations with ease.&lt;/p&gt;
&lt;p&gt;For example, I store my work projects in &lt;code&gt;~/workspaces/work&lt;/code&gt; and my personal projects in &lt;code&gt;~/workspaces/personal&lt;/code&gt;. In my &lt;code&gt;~/.gitconfig&lt;/code&gt;, I added the following &lt;code&gt;[includeIf]&lt;/code&gt; statements:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[includeIf &quot;gitdir:~/workspaces/work/&quot;]
  path = ~/.config/git/work-config

[includeIf &quot;gitdir:~/workspaces/personal/&quot;]
  path = ~/.config/git/personal-config

# ...all other Git settings
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tells Git to include the configuration file at the specified path when I&apos;m working in the matching directory.&lt;/p&gt;
&lt;p&gt;For example, in &lt;code&gt;~/.config/git/personal-config&lt;/code&gt;, I set my GitHub email address and signing key:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[user]
  email = kewah@users.noreply.github.com
  signingkey = ssh-ed25519 AAAAweriaksjldhfkjasbvlkasbv
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, when I&apos;m working in the &lt;code&gt;~/workspaces/personal&lt;/code&gt; directory, Git automatically uses the &lt;code&gt;[user]&lt;/code&gt; settings in &lt;code&gt;~/.config/git/personal-config&lt;/code&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Setting up Vercel Analytics in an Astro project</title><link>https://kewah.com/setting-up-vercel-analytics-in-astro/</link><guid isPermaLink="true">https://kewah.com/setting-up-vercel-analytics-in-astro/</guid><description>How to fix the `[Analytics] VERCEL_ANALYTICS_ID not found error` that I encountered when setting up Vercel Analytics in an Astro project.
</description><pubDate>Wed, 19 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have recently migrated my website to host it on Vercel. I was interested in experimenting with the &lt;a href=&quot;https://vercel.com/analytics&quot;&gt;analytics service&lt;/a&gt; that tracks &lt;a href=&quot;https://web.dev/vitals/&quot;&gt;Web Vitals&lt;/a&gt;. It&apos;s not crucial for this lightweight static website—it even adds a JavaScript script—but I wanted to see what data we get from it.&lt;/p&gt;
&lt;p&gt;It was straightforward to set up following the Astro &lt;a href=&quot;https://docs.astro.build/en/guides/integrations-guide/vercel/&quot;&gt;documentation page&lt;/a&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install the dependency&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;pnpm add @astrojs/vercel
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Update the &lt;code&gt;astro.config.mjs&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;import { defineConfig } from &apos;astro/config&apos;;
import vercel from &apos;@astrojs/vercel/static&apos;;

export default defineConfig({
  output: &apos;static&apos;,
  adapter: vercel({ analytics: true }),
});
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Enable &lt;a href=&quot;https://vercel.com/docs/concepts/analytics/quickstart&quot;&gt;Vercel Analytics&lt;/a&gt; in the project in the Vercel interface&lt;/li&gt;
&lt;li&gt;Deploy and profit!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Well, not yet for me—I got the error &lt;code&gt;[Analytics] VERCEL_ANALYTICS_ID not found&lt;/code&gt; in the dev tools console.&lt;/p&gt;
&lt;p&gt;When running the build command, the &lt;a href=&quot;https://github.com/withastro/astro/blob/8cc53090caea3ba08e85e2cc8f47db644e2aba74/packages/integrations/vercel/src/analytics.ts#L42&quot;&gt;line&lt;/a&gt; &lt;code&gt;const analyticsId = import.meta.env.PUBLIC_VERCEL_ANALYTICS_ID&lt;/code&gt; got minified to &lt;code&gt;e={}.PUBLIC_VERCEL_ANALYTICS_ID&lt;/code&gt;. To fix it, I needed define the custom variable in the &lt;code&gt;astro.config.mjs&lt;/code&gt; to replace it with &lt;code&gt;process.env.VERCEL_ANALYTICS_ID&lt;/code&gt; env var passed by Vercel on build time.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import vercel from &apos;@astrojs/vercel/static&apos;;
import { defineConfig } from &apos;astro/config&apos;;

export default defineConfig({
  output: &apos;static&apos;,
  adapter: vercel({ analytics: true }),
  vite: {
    define: {
      &apos;import.meta.env.PUBLIC_VERCEL_ANALYTICS_ID&apos;: JSON.stringify(process.env.VERCEL_ANALYTICS_ID),
    },
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alright, now it works!&lt;/p&gt;
</content:encoded></item><item><title>Back to RSS</title><link>https://kewah.com/back-to-rss/</link><guid isPermaLink="true">https://kewah.com/back-to-rss/</guid><description>With the recent events at Twitter, I decided to stop using the social network and explore decentralized social media alternatives. I plan to use RSS to stay up-to-date on my favourite topics.
</description><pubDate>Fri, 06 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Following the recent events at Twitter, I&apos;ve chosen to delete all my content and discontinue using the platform. However, I haven&apos;t been able to fully delete my account yet. Nevertheless, I still feel nostalgic about what Twitter once symbolized. It played a vital role in my online journey for more than ten years, enabling me to connect with fellow developers and establish communities.&lt;/p&gt;
&lt;p&gt;Now, with a compelling reason to curb my Twitter dependency, I find myself at an opportune moment to reevaluate my information consumption and sharing habits.&lt;/p&gt;
&lt;p&gt;Over time, I&apos;ve grown increasingly vigilant about how products manage my data. Gradually, I&apos;ve migrated towards services that prioritize privacy, such as &lt;a href=&quot;https://www.fastmail.com/&quot;&gt;Fastmail&lt;/a&gt; and &lt;a href=&quot;https://duckduckgo.com/&quot;&gt;DuckDuckGo&lt;/a&gt;, and more recently, local-first applications like &lt;a href=&quot;https://logseq.com/&quot;&gt;LogSeq&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The decentralized social media space is evolving rapidly with new protocols and services being announced. &lt;a href=&quot;https://joinmastodon.org/&quot;&gt;Mastodon&lt;/a&gt; is currently receiving the most attention. I&apos;ve started using it again (&lt;a href=&quot;https://mamot.fr/@kewah/109262528822305988&quot;&gt;my fourth attempt&lt;/a&gt;) as more people I follow on Twitter are active there. However, I don&apos;t want to settle for it—I want to focus my attention and effort on returning to RSS feeds.&lt;/p&gt;
&lt;p&gt;RSS strikes the right balance of decentralization for me. It enables individuals to host their content wherever they prefer, ensuring control over ownership. All that&apos;s required is to expose an XML file. The rest is in our hands—we can construct everything ourselves or employ services to assist us with publishing. While it might not be the most efficient format for discovery, it has demonstrated its reliability and currently powers the podcasting industry (despite Spotify&apos;s attempts to monopolize it with their walled garden).&lt;/p&gt;
&lt;p&gt;I&apos;m confident that RSS will satisfy my need to stay current and learn from other professionals in the web industry. I&apos;ve added all my favorite blogs to &lt;a href=&quot;https://reederapp.com/&quot;&gt;Reeder&lt;/a&gt; and subscribed to various link aggregators&apos; newsletters[^1] with an RSS feed to uncover new content and broaden my horizons.&lt;/p&gt;
&lt;p&gt;It seems like we&apos;re entering a new era of social media, and I&apos;m excited to see its evolution. Meanwhile, I&apos;ve returned to using RSS.&lt;/p&gt;
&lt;p&gt;[^1]: &lt;a href=&quot;https://us1.campaign-archive.com/home/?u=faa8eb4ef3a111cef92c4f3d4&amp;amp;id=e505c88a2e&quot;&gt;Hackernewsletter daily&lt;/a&gt;, &lt;a href=&quot;https://programmingdigest.net/&quot;&gt;Programming digest&lt;/a&gt;, &lt;a href=&quot;https://calibreapp.com/newsletter&quot;&gt;Performance newsletter&lt;/a&gt;, &lt;a href=&quot;https://reactdigest.net/&quot;&gt;React digest&lt;/a&gt;, and &lt;a href=&quot;https://typescript-weekly.com/&quot;&gt;TypeScript weekly&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Engineers need to write well</title><link>https://kewah.com/engineers-need-to-write-well/</link><guid isPermaLink="true">https://kewah.com/engineers-need-to-write-well/</guid><description>As engineers, we spend a lot of time writing. We must master that skill as much as code. This will help us collaborate and leverage our work within our teams or organizations.
</description><pubDate>Sun, 19 Dec 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In a remote-first company like &lt;a href=&quot;https://acast.com&quot;&gt;Acast&lt;/a&gt;, written communication is an essential skill to master at any level—from IC1 to CEO. We write Shortcut tickets, Slack messages, commit messages, PR reviews, or feedback throughout the day. Our work won&apos;t be leveraged or impactful if we can&apos;t share ideas, arguments, or information. When we can&apos;t communicate clearly, our value in a team is drastically diminished. As we move to senior roles, it becomes critical.&lt;/p&gt;
&lt;h2&gt;Taking the time to write&lt;/h2&gt;
&lt;p&gt;There is no rush. No one is expected to respond within a minute. In the same way, when we refactor while coding, we can take the time to review what we are writing. Remember that while we write something once, a variety of people will see it many times. Making sure that the context is understood and that the message is focused on the subject is vital. Like code, we want to share our box model with the next person reading it. Readers must switch contexts from what they were doing to read a message, which requires a lot of energy. Our goal is to reduce that effort. Although it&apos;s more work, it&apos;ll be easier for people to provide feedback and build ideas on top.&lt;/p&gt;
&lt;h2&gt;Writing for thinking&lt;/h2&gt;
&lt;p&gt;Connecting ideas or developing arguments can be tricky on the first try. We may even have more questions than we did when we started. However, writing clarifies our thoughts, helping us explain them more easily. An excellent quote from Leslie Lamport challenged my view in the past.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you think without writing, you only think you&apos;re thinking.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It&apos;s a long journey. It will take an active effort to think about what we write, but we need to practice every day to improve. The goal is not to be a novelist. However, we can still use relevant tips to improve our communication at work every day.&lt;/p&gt;
&lt;p&gt;The following resources help me improve my writing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Writing-Well-Classic-Guide-Nonfiction/dp/0060891548&quot;&gt;On writing well&lt;/a&gt; by William Zinsser.&lt;/li&gt;
&lt;li&gt;If you prefer a shorter version, Julian Shapiro condensed a lot of information in the &lt;a href=&quot;https://www.julian.com/guide/write/intro&quot;&gt;writing well handbook&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;He is also active on Twitter and often shares threads that extract notes from his handbook. For instance, &lt;a href=&quot;https://threads-web.vercel.app/threads/1356979505475313665&quot;&gt;&quot;How to rewrite bad writing&quot;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Finally, I heavily use &lt;a href=&quot;https://grammarly.com&quot;&gt;Grammarly&lt;/a&gt;. They have helpful suggestions and correction algorithms.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>CDK pattern – Caching static assets with AWS S3 and CloudFront</title><link>https://kewah.com/cdk-pattern-static-files-s3-cloudfront/</link><guid isPermaLink="true">https://kewah.com/cdk-pattern-static-files-s3-cloudfront/</guid><description>CDK Pattern to setup CloudFront and S3 to serve static files.
</description><pubDate>Fri, 30 Jul 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The code below is a pattern for client-side applications or for serving static files. We want to serve files through a CDN and cache them for an extended period.&lt;/p&gt;
&lt;p&gt;In the AWS ecosystem, we need to store the files in S3 and set the &lt;code&gt;cache-control&lt;/code&gt; header to tell the CloudFront distribution how long it should cache the file.&lt;/p&gt;
&lt;p&gt;It’s recommended to include a hash in the filename, for instance, using &lt;a href=&quot;https://webpack.js.org/guides/caching/#output-filenames&quot;&gt;&lt;code&gt;[contenthash]&lt;/code&gt;&lt;/a&gt; in webpack, so we only update changed files and prevent locking the user in an old version.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import * as acm from &apos;@aws-cdk/aws-certificatemanager&apos;;
import * as cloudfront from &apos;@aws-cdk/aws-cloudfront&apos;;
import * as iam from &apos;@aws-cdk/aws-iam&apos;;
import * as route53 from &apos;@aws-cdk/aws-route53&apos;;
import * as targets from &apos;@aws-cdk/aws-route53-targets&apos;;
import * as s3 from &apos;@aws-cdk/aws-s3&apos;;
import * as s3Deploy from &apos;@aws-cdk/aws-s3-deployment&apos;;
import { CacheControl } from &apos;@aws-cdk/aws-s3-deployment&apos;;
import * as cdk from &apos;@aws-cdk/core&apos;;
import { Duration } from &apos;@aws-cdk/core&apos;;
import config from &apos;config&apos;;

export class StaticFilesStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const cdnDomain = config.get(&apos;domain&apos;);
    new cdk.CfnOutput(this, &apos;Site&apos;, { value: `https://${cdnDomain}` });

    const cloudfrontOAI = new cloudfront.OriginAccessIdentity(this, &apos;cloudfront-OAI&apos;, {
      comment: `OAI for ${cdnDomain}`,
    });

    const siteBucket = new s3.Bucket(this, &apos;SiteBucket&apos;, {
      // We only allow traffic to the bucket through CloudFront.
      publicReadAccess: false,
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      websiteIndexDocument: &apos;index.html&apos;,
      websiteErrorDocument: &apos;index.html&apos;,
    });

    // Grant access to CloudFront.
    siteBucket.addToResourcePolicy(
      new iam.PolicyStatement({
        actions: [&apos;s3:GetObject&apos;],
        resources: [siteBucket.arnForObjects(&apos;*&apos;)],
        principals: [
          new iam.CanonicalUserPrincipal(cloudfrontOAI.cloudFrontOriginAccessIdentityS3CanonicalUserId),
        ],
      })
    );
    new cdk.CfnOutput(this, &apos;Bucket&apos;, { value: siteBucket.bucketName });

    const hostedZone = route53.HostedZone.fromLookup(this, &apos;hostedZone&apos;, {
      domainName: config.get(&apos;domainZone&apos;),
    });

    const certificate = acm.Certificate.fromCertificateArn(this, &apos;Certificate&apos;, config.get(&apos;certificate&apos;));

    const distribution = new cloudfront.CloudFrontWebDistribution(this, &apos;SiteDistribution&apos;, {
      viewerCertificate: cloudfront.ViewerCertificate.fromAcmCertificate(certificate, {
        aliases: [cdnDomain],
      }),
      viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
      originConfigs: [
        {
          s3OriginSource: {
            s3BucketSource: siteBucket,
            originAccessIdentity: cloudfrontOAI,
          },
          behaviors: [{ isDefaultBehavior: true }],
        },
      ],
    });
    new cdk.CfnOutput(this, &apos;CloudFrontUrl&apos;, {
      value: `https://${distribution.distributionDomainName}`,
    });

    new route53.ARecord(this, &apos;SiteAliasRecord&apos;, {
      recordName: cdnDomain,
      target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(distribution)),
      zone: hostedZone,
    });

    // We set the long cache-control header for all files except the index.html.
    new s3Deploy.BucketDeployment(this, &apos;BucketDeploymentWithCache&apos;, {
      sources: [
        // `dist` is the webpack output folder.
        s3Deploy.Source.asset(&apos;dist&apos;, { exclude: [&apos;index.html&apos;] }),
      ],
      destinationBucket: siteBucket,
      distribution,
      distributionPaths: [&apos;/*&apos;],
      cacheControl: [
        CacheControl.setPublic(),
        CacheControl.maxAge(cdk.Duration.days(365)),
        CacheControl.fromString(&apos;immutable&apos;),
      ],
      prune: false,
    });

    // Set the short cache-control header for the index.html.
    // In this example I put the cache to 0 seconds, but you should adapt it to your needs.
    new s3Deploy.BucketDeployment(this, &apos;BucketDeploymentNoCache&apos;, {
      sources: [
        s3Deploy.Source.asset(&apos;dist&apos;, {
          exclude: [&apos;*&apos;, &apos;!index.html&apos;],
        }),
      ],
      destinationBucket: siteBucket,
      distribution,
      distributionPaths: [&apos;/*&apos;],
      cacheControl: [
        CacheControl.setPublic(),
        CacheControl.maxAge(cdk.Duration.seconds(0)),
        CacheControl.sMaxAge(cdk.Duration.seconds(0)),
      ],
      prune: false,
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the stack is deployed, we can check the response header contains the correct values and hit the cache.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HTTP/2 200 OK
content-type: application/javascript
date: Fri, 30 Jul 2021 16:56:43 GMT
last-modified: Fri, 30 Jul 2021 16:56:40 GMT
etag: W/&quot;12345&quot;
// highlight-start
cache-control: public, max-age=31536000, immutable
// highlight-end
server: AmazonS3
content-encoding: gzip
vary: Accept-Encoding
// highlight-start
x-cache: Hit from cloudfront
// highlight-end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using &lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/&quot;&gt;Lighthouse&lt;/a&gt;, we can also verify that we don’t get any warning related to “serving static assets with an efficient cache policy.”&lt;/p&gt;
</content:encoded></item><item><title>AWS CloudFront Functions</title><link>https://kewah.com/aws-cloudfront-functions/</link><guid isPermaLink="true">https://kewah.com/aws-cloudfront-functions/</guid><description>My notes about the CloudFront Functions that AWS recently released.
</description><pubDate>Fri, 14 May 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;AWS recently released &lt;a href=&quot;https://aws.amazon.com/blogs/aws/introducing-cloudfront-functions-run-your-code-at-the-edge-with-low-latency-at-any-scale/&quot;&gt;CloudFront Functions&lt;/a&gt;—a new serverless scripting platform that allows us to run lightweight JavaScript code at the CloudFront edge locations. These new functions are designed for low latency web request customization.&lt;/p&gt;
&lt;h2&gt;What&apos;s the difference with Lambda@Edge?&lt;/h2&gt;
&lt;p&gt;CloudFront Functions and Lambda@Edge are both triggered by events generated by CloudFront. But, CloudFront Functions sit in front of the CloudFront cache. The functions are executed on every request or every response—that&apos;s why CloudFront Functions need to run under 1ms. In comparison, a Lambda sits between the CloudFront cache and the origin, so it&apos;s executed only when the cache is not valid.&lt;/p&gt;
&lt;p&gt;
—
&lt;a href=&quot;https://aws.amazon.com/blogs/aws/introducing-cloudfront-functions-run-your-code-at-the-edge-with-low-latency-at-any-scale/&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;AWS built the CloudFront Functions to address scaling and performance issues that we could face with Lambda@Edge. For instance, cold starts would impact the performance for doing token authorization. Or, in the case of high traffic spikes, we would be limited by lambda throttling or concurrency. With CloudFront Functions, no matter how many requests we get, it will scale indefinitely to match the traffic.&lt;/p&gt;
&lt;p&gt;
—
&lt;a href=&quot;https://aws.amazon.com/blogs/aws/introducing-cloudfront-functions-run-your-code-at-the-edge-with-low-latency-at-any-scale/&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;When to use CloudFront Functions?&lt;/h2&gt;
&lt;p&gt;If we need to rewrite (i.e., AB flag), redirect, authorize (i.e., JWT token) a request, we should consider using a CloudFront Function.&lt;/p&gt;
&lt;p&gt;If we need to do heavier computing, like server-side rendering, or data aggregation/transformation that wouldn&apos;t fit the performance budget, we should use a Lambda@Edge.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Resources&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AWS announcement article: &lt;a href=&quot;https://aws.amazon.com/blogs/aws/introducing-cloudfront-functions-run-your-code-at-the-edge-with-low-latency-at-any-scale/&quot;&gt;Introducing CloudFront Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;AWS Tech podcast episode: &lt;a href=&quot;https://play.acast.com/s/awstechchat/tag%3Asoundcloud%2C2010%3Atracks%2F1045939213&quot;&gt;CloudFront Functions Edge Computing Special&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>API Gateway-first approach</title><link>https://kewah.com/api-gateway-first-approach/</link><guid isPermaLink="true">https://kewah.com/api-gateway-first-approach/</guid><description>The API Gateway-first approach is the idea that we should first consider using API Gateway when building new services.
</description><pubDate>Thu, 25 Feb 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The &quot;API Gateway-first approach&quot; is the idea that, in the context of an AWS cloud stack, when we build a new service, we first consider using &lt;a href=&quot;https://aws.amazon.com/api-gateway/&quot;&gt;API Gateway&lt;/a&gt;. If it doesn&apos;t fit our use case, we can consider something else, such as a &lt;a href=&quot;https://aws.amazon.com/elasticloadbalancing&quot;&gt;load balancer&lt;/a&gt; in front of a Node.js server running on &lt;a href=&quot;https://aws.amazon.com/fargate/&quot;&gt;Fargate&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;API gateway is a powerful service and by far my favorite of the AWS ecosystem. It is the glue that connects a web request to another AWS service—like Lambda or DynamoDB—which gives us the ability to take granular design decisions for each endpoint of our service. It helps us define strict boundaries in our code to leave the orchestration code out of our business logic code—handled in a Lambda, for example.&lt;/p&gt;
&lt;p&gt;If you are used to building Node.js microservices, you can see API Gateway as the combination of a load balancer and a routing system (&lt;a href=&quot;https://expressjs.com/en/guide/routing.html&quot;&gt;express router&lt;/a&gt; or
&lt;a href=&quot;https://roamresearch.com/#/app/kewah/page/4KlmUyp9o&quot;&gt;koa router&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;What I like about API Gateway:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AWS fully manages it, so we don&apos;t need to handle its availability, and we can set it up using CloudFormation (CDK).&lt;/li&gt;
&lt;li&gt;We can collocate different stacks under the same domain. For instance, we can run a React application (Fargate) alongside a REST API (Lambda + DynamoDB). That allows us to choose different solutions depending on the endpoint&apos;s need. Therefore it will be easier to maintain the service over time because we can replace components separately. It is worth noting that API Gateway can proxy to different types of AWS services, so we don&apos;t necessarily need to use a Lambda. A rule of thumbs is that if the Lambda is only responsible for passing data from one service to another, it&apos;s a sign that we can use an API Gateway integration. For instance, we can directly map a request coming through API Gateway to &lt;a href=&quot;https://medium.com/brlink/rest-api-just-with-apigateway-and-dynamodb-8a9b0cd76b7a&quot;&gt;DynamoDB using mapping templates&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;We can scale at the endpoint level instead of at the service level. However, like any serverless service, we can&apos;t scale indefinitely—we need to consider the API Gateway request per second limit and concurrency burst limit.&lt;/li&gt;
&lt;li&gt;We can grant different permissions for each endpoint to reduce the blast radius if the service is compromised (following &lt;a href=&quot;https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege&quot;&gt;the least privilege principle&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;It&apos;s the perfect candidate to apply the &lt;a href=&quot;https://martinfowler.com/bliki/StranglerFigApplication.html&quot;&gt;strangler pattern&lt;/a&gt; for migrating a legacy application to a new stack. When we start the migration, API Gateway will proxy all queries to the legacy service. Piece by piece, we can migrate each endpoint individually to point to a new integration.&lt;/li&gt;
&lt;li&gt;There is integrated support with CloudFront (for caching and running our service at the edge) and CloudWatch (to track errors and other metrics), so it is easier to set up, and we have less boilerplate code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, API Gateway is not a silver bullet and has some limitations. Remember that it is a &quot;first approach,&quot; not an &quot;only approach.&quot; Drawbacks of API Gateway:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It connects different services with their limitation (API throttling, timeout, etc.), impacting performances or leading to failure. When designing a system, we also need to take them into account.&lt;/li&gt;
&lt;li&gt;Since each endpoint can be a different integration, it makes testing more challenging. For instance, if we use API Gateway + DynamoDB we cannot run it locally. We need to deploy a test stack against which we run our tests (end-to-end tests). It gets slow to run on a CI because deploying and destroying a stack takes several minutes. Some solutions, like &lt;a href=&quot;https://github.com/localstack/localstack&quot;&gt;LocalStack&lt;/a&gt;, can mock different AWS services locally, which will reduce the time to run the tests, but we lose the benefits of testing against the real environment.&lt;/li&gt;
&lt;li&gt;The possibility to use a mapping template to transform the data from API Gateway to another service is excellent in theory. Because it removes the need to build and monitor another Lambda, and we leave it to AWS to fully handle the transaction. However, the template language can be tricky to use and debug. Since we are writing a string, our editor has no support to help us figure out what data we&apos;re handling. The only source of information is the &lt;a href=&quot;https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html&quot;&gt;documentation&lt;/a&gt; page. So, the learning curve can be steep, and depending on your needs, it might be simpler to use a Lambda.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next time you start to build a new service before jumping on Node.js, Express, and Docker, think about it, can you use API Gateway instead?&lt;/p&gt;
</content:encoded></item><item><title>Speed up your CircleCI pipeline by using the RAM disk</title><link>https://kewah.com/faster-circleci-pipeline-using-the-ram-disk/</link><guid isPermaLink="true">https://kewah.com/faster-circleci-pipeline-using-the-ram-disk/</guid><description>By using the RAM disk on CircleCI, we can reduce the time for restoring the cache. In my current project, it has reduced the time to run the pipeline by 2 minutes.
</description><pubDate>Thu, 21 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;During a presentation—held internally at &lt;a href=&quot;https://www.acast.com&quot;&gt;Acast&lt;/a&gt; by my colleague &lt;a href=&quot;https://www.linkedin.com/in/danielgrefberg/&quot;&gt;Daniel Grefberg&lt;/a&gt;—I discovered a one-line change that reduced by 2 minutes the execution time of my current project&apos;s CI pipeline.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- working_directory: ~/my-project-name
+ working_directory: /mnt/ramdisk/my-project-name
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://circleci.com/&quot;&gt;CircleCI&lt;/a&gt; allows us to mount the project on the &lt;a href=&quot;https://circleci.com/docs/2.0/executor-types/#ram-disks&quot;&gt;RAM disk&lt;/a&gt; and use it as a &quot;&lt;a href=&quot;https://en.wikipedia.org/wiki/Tmpfs&quot;&gt;temporary file storage paradigm&lt;/a&gt;.&quot; That means, when caching a project&apos;s dependencies, CircleCI will use the RAM disk instead of the disk drive.&lt;/p&gt;
&lt;p&gt;When working on a Node.js project, it is common to cache the &lt;code&gt;node_modules&lt;/code&gt; folder. It allows us to re-use it in the other jobs to avoid running the install command every time. On paper, that should be faster than running &lt;code&gt;npm ci&lt;/code&gt; (or &lt;code&gt;yarn install --frozen-lockfile&lt;/code&gt;) for each job, but that&apos;s not necessarily the case. It usually takes ~2 minutes to restore the cache of ~400MB. This time is mainly taken for decompressing the thousands of files from the folder on the disk. But, this step gets down to ~20 seconds by moving it to the RAM disk.&lt;/p&gt;
&lt;p&gt;This change is a quick win to save a few minutes for running our pipeline. The only drawback is that we are limited to the RAM capacity—defined by the &lt;a href=&quot;https://circleci.com/docs/2.0/configuration-reference/#resource_class&quot;&gt;job&apos;s resource_class attribute&lt;/a&gt;. By default, it runs on a 4GB RAM (medium resource class), so depending on our project and the dependencies&apos; size, we might need to increase the resource class.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version: 2.1

aliases:
  - &amp;amp;default_image
    working_directory: /mnt/ramdisk/my-project
    docker:
      - image: cimg/node:12.20

  - &amp;amp;cache_key v1-cache-{{checksum &quot;.circleci/config.yml&quot;}}-{{checksum
    &quot;yarn.lock&quot;}}
  - &amp;amp;restore_cache
    restore_cache:
      keys:
        - *cache_key

jobs:
  initialize:
    &amp;lt;&amp;lt;: *default_image
    steps:
      - checkout
      - *restore_cache
      - run: yarn install --frozen-lockfile
      - save_cache:
          name: Save cache
          key: *cache_key
          paths:
            - node_modules

  test_unit:
    &amp;lt;&amp;lt;: *default_image
    steps:
      - checkout
      - *restore_cache
      - run: yarn test:unit --maxWorkers=2
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Write more, but shorter</title><link>https://kewah.com/write-more-but-shorter/</link><guid isPermaLink="true">https://kewah.com/write-more-but-shorter/</guid><description>After the &quot;keep it simple&quot; in programming, the &quot;keep it short&quot; for writing.
</description><pubDate>Sun, 17 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently stumbled upon an article from Mike Crittenden, &lt;a href=&quot;https://critter.blog/2020/10/02/write-5x-more-but-write-5x-less/&quot;&gt;Write 5x more but write 5x less&lt;/a&gt;, in which he shortly explains why we should write more, but shorter.&lt;/p&gt;
&lt;p&gt;It’s a concept that resonates a lot with me. Since I started to follow the &lt;a href=&quot;https://zettelkasten.de/posts/overview/&quot;&gt;Zettelkasten&lt;/a&gt; method for taking notes, it became clear that I must write to have clearer thoughts. It helps me to be sure that I understand a concept by explaining it. By using the notes I create, I also aim to publish more articles on my website. That’s a great way to feel accountable and push me to be as straightforward as possible because someone else could read it.&lt;/p&gt;
&lt;p&gt;By writing more, I&apos;ll articulate better my thinking and sharpen my writing skill over time. It&apos;s even more critical now that I work remotely and that most of my communication with my team is done by writing. It will also feel more natural for me to write things down because I will build the habit of writing. (By building habits, we don&apos;t rely on motivation to do something.)&lt;/p&gt;
&lt;p&gt;By writing shorter content, I&apos;ll reduce my expectation on how long an article should be before publishing it. It&apos;s also a great exercise to briefly explain an idea or concept to make the content more digestible.&lt;/p&gt;
&lt;p&gt;After the &quot;keep it simple&quot; in programming, the &quot;keep it short&quot; for writing.&lt;/p&gt;
</content:encoded></item><item><title>Roam is my space for organized chaos</title><link>https://kewah.com/roam-organized-chaos/</link><guid isPermaLink="true">https://kewah.com/roam-organized-chaos/</guid><description>I talk about why I use Roam and consider it as my space for organized chaos.
</description><pubDate>Sat, 02 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;span class=&quot;intro&quot;&amp;gt;
For the past six months, I have been using &amp;lt;a href=&quot;https://roamresearch.com&quot;&amp;gt;Roam&amp;lt;/a&amp;gt; as my primary application for note-taking and knowledge management. This article will share what drove me to start using Roam and what made me stick to it.
&amp;lt;/span&amp;gt;&lt;/p&gt;
&lt;h2&gt;My road to Roam&lt;/h2&gt;
&lt;p&gt;From my interest and work, I read many articles, books or listen to podcasts. But, apart from highlighting some quotes, I didn&apos;t extract much of it. Moreover, I struggled to remember where I could find the information afterward.&lt;/p&gt;
&lt;p&gt;At the end of 2019, I stumbled upon the &lt;a href=&quot;https://zettelkasten.de/posts/overview/&quot;&gt;Zettelkasten&lt;/a&gt; method while reading the book &lt;a href=&quot;https://takesmartnotes.com/&quot;&gt;How to Take Smart Notes&lt;/a&gt;. This method aims to write one idea per note and organize them using a keyword index to link them together. The goal is to synthesize what we learn for improving retention of information and connect ideas that could, in the first place, look unrelated.&lt;/p&gt;
&lt;p&gt;Multiple members of the Zettelkasten&apos;s community, including &lt;a href=&quot;https://roamresearch.com/#/app/kewah/page/7kfbKZtur&quot;&gt;Sönke Ahrens&lt;/a&gt;, advised using Roam for implementing the method. So, I decided to give it a try and experiment with it.&lt;/p&gt;
&lt;h2&gt;Connecting the dots&lt;/h2&gt;
&lt;p&gt;Roam has a different approach than other applications that I used in the past—Evernote, Apple Notes, and Notion—because It doesn&apos;t have a file or folder system. It only has pages that can be linked together using hyperlinks. One of Roam&apos;s significant features is that linking is bidirectional; when adding a link from a page, it will create a backlink reference on the other page.&lt;/p&gt;
&lt;p&gt; &lt;em&gt;Example of a backlink in my page &lt;a href=&quot;https://maggieappleton.com/&quot;&gt;Maggie Appleton&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The backlink system reminds me of how I use my text editor for inspecting a codebase to build the program&apos;s mental model or part of it. I mentally visualize a program as a graph—I go to different nodes by following where a function is implemented or checking where a function is referenced. The linking system in Roam allows me to do that in my notes. I can navigate to a page by following the hyperlink or find where I reference it by looking at the bottom of the page where Roam put the linked references.&lt;/p&gt;
&lt;p&gt;Another key part of Roam is its blocks (they are like bullet points that compose a page). For me, that&apos;s where Roam&apos;s power lies compared to other applications. It assigns a unique ID to each block, so we can link them together or even embed them in different pages. Thus we can be more granular when linking ideas, quotes, or notes and put them in a different context.&lt;/p&gt;
&lt;p&gt;At first, I created a new page for each note to follow the &lt;a href=&quot;https://zettelkasten.de/posts/create-zettel-from-reading-notes/&quot;&gt;Principle of Atomicity&lt;/a&gt;. But, with the blocks, I can also apply this principle at their level. Now, most of my writing happens in the daily notes pages; I create pages for keywords to reference the content or my &lt;a href=&quot;https://maggieappleton.com/evergreens/&quot;&gt;evergreen notes&lt;/a&gt;. The mix of backlinks and block references removed a lot of friction as I don&apos;t need to overthink where a note should live. I know that I&apos;ll be able to find it when searching for it using specific keywords.&lt;/p&gt;
&lt;p&gt; &lt;em&gt;Screenshot of a daily note page containing reference notes&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;The needle in a haystack&lt;/h2&gt;
&lt;p&gt;The keywords are a central part of my workflow in Roam. I avoid using a metadata section where I will put down a list of tags. I prefer the verbosity of writing a sentence about why this note is related to a term. It brings more context that will help me identify if I need this specific note. I find the tagging system too disconnected from the content; it takes more time and energy to figure out what this note is about to tag and evaluate.&lt;/p&gt;
&lt;p&gt;To find content in Roam, we can either use backlink reference, the search bar or use the query feature. Queries give us the possibility to ask Roam to surface all content that contains links to a given page. Their significant advantage is that we embed them in our pages so that they won&apos;t disappear—unlike with the search bar—and they can live next to our contents. This feature is handy when we need to gather the references for an article or, for instance, if we want to have a page where we can find &lt;a href=&quot;https://www.youtube.com/watch?v=D8lGWF1N0PU&quot;&gt;overdue tasks&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt; &lt;em&gt;Example of a query to gather notes for the article.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you are interested to learn more about Roam&apos;s queries, I recommend watching the &lt;a href=&quot;https://www.youtube.com/watch?v=LJZBGJOzhUY&quot;&gt;video&lt;/a&gt; made by &lt;a href=&quot;https://robhaisfield.com/about&quot;&gt;Rob Haisfield&lt;/a&gt;. It covers the different operators, use cases, and workarounds that you might need to improve the search results. The syntax might be a bit unsettling at first glance, but after one or two queries written, it quickly became my go-to search feature.&lt;/p&gt;
&lt;h3&gt;My messy room&lt;/h3&gt;
&lt;p&gt;Roam is my space for organized chaos: it&apos;s not pretty, it doesn&apos;t look ordered for someone else than me, but I have the confidence that I&apos;ll find and won&apos;t lose anything in there. And that&apos;s something I have struggled with in the past with other applications.&lt;/p&gt;
&lt;p&gt;Relying only on links and blocks brings a lot of flexibility. It allows me to change the structure over time without spending countless hours moving things around. From the beginning, my goal was to use it as a knowledge management tool. Although I recently started to follow the &lt;a href=&quot;https://nesslabs.com/interstitial-journaling&quot;&gt;interstitial journaling&lt;/a&gt; workflow. It combines my note-taking, task management, and time tracking from the daily notes. That will be an excellent way to expand my use case and experiment with managing my day in Roam.&lt;/p&gt;
&lt;p&gt;Roam is still rough on the edge, but I like how the team behind it experiments with new features. They also provide the possibility to inject JavaScript scripts and override CSS in the application, so it opens many options to improve the UX in a hacky way. I look forward to seeing how Roam will continue evolving. I can&apos;t wait for the team to release a public API and see how the community could enhance the experience.&lt;/p&gt;
</content:encoded></item><item><title>CDK pattern – API Gateway caching</title><link>https://kewah.com/api-gateway-caching-with-aws-cdk/</link><guid isPermaLink="true">https://kewah.com/api-gateway-caching-with-aws-cdk/</guid><description>Example of a AWS CDK to add caching on your API Gateway endpoint that takes query string into account.
</description><pubDate>Fri, 05 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;API Gateway has built-in support for caching endpoint’s responses. So, we don’t have to set up CloudFront by ourselves.&lt;/p&gt;
&lt;h2&gt;Enable API Gateway caching&lt;/h2&gt;
&lt;p&gt;To enable caching, we need to use an edge optimized endpoint and set some values on the &lt;code&gt;deployOptions&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const api = new apigateway.RestApi(this, id, {
  domainName: {
    endpointType: apigateway.EndpointType.EDGE,
  },
  deployOptions: {
    cachingEnabled: true,
    cacheClusterEnabled: true,
    cacheTtl: cdk.Duration.minutes(30),
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that if we need to use a &lt;a href=&quot;https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-edge-optimized-custom-domain-name.html&quot;&gt;custom domain&lt;/a&gt; for our API the certificate needs to be in the &lt;code&gt;us-east-1&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To use an ACM certificate with an API Gateway edge-optimized custom domain name, you must request or import the certificate in the us-east-1 Region (US East (N. Virginia)). To enable caching with need to use an edge optimized endpoint.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We can now query our endpoint and check CloudWatch to check that we are hitting the cache.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Query string support&lt;/h2&gt;
&lt;p&gt;That works great until we start to use query strings. For instance, if we build an &lt;a href=&quot;https://oembed.com/&quot;&gt;OEmbed&lt;/a&gt; API, we need to support &lt;code&gt;url&lt;/code&gt; and &lt;code&gt;format&lt;/code&gt; query strings. With the current setup, we will always get the same cached response when hitting the endpoint even if we change the &lt;code&gt;url&lt;/code&gt; value.&lt;/p&gt;
&lt;p&gt;To fix that, we need to set up the request parameters to take the query strings into account.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const integration = new apigateway.LambdaIntegration(lambda, {
  cacheKeyParameters: [&apos;method.request.path.url&apos;, &apos;method.request.path.format&apos;],
  requestParameters: {
    &apos;integration.request.path.url&apos;: &apos;method.request.path.url&apos;,
    &apos;integration.request.path.format&apos;: &apos;method.request.path.format&apos;,
  },
});
endpoint.addMethod(&apos;GET&apos;, integration, {
  requestParameters: {
    &apos;method.request.path.url&apos;: true,
    &apos;method.request.path.format&apos;: true,
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;If you know a less verbose solution, please don&apos;t hesitate to ping me on Mastodon.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;All together solution&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;export class Stack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const lambda = new lambda.Function(this, &apos;SomeFunctionId&apos;, {
      runtime: lambda.Runtime.NODEJS_12_X,
      code: lambda.Code.fromAsset(&apos;functions/dist/handler&apos;),
      handler: &apos;index.handler&apos;,
    });

    const api = new apigateway.RestApi(this, id, {
      domainName: {
        endpointType: apigateway.EndpointType.EDGE,
      },
      deployOptions: {
        cachingEnabled: true,
        cacheClusterEnabled: true,
        cacheTtl: cdk.Duration.minutes(30),
      },
    });
    const v1 = api.root.addResource(&apos;v1&apos;);
    const endpoint = v1.addResource(&apos;some-route&apos;);

    const integration = new apigateway.LambdaIntegration(lambda, {
      cacheKeyParameters: [&apos;method.request.path.url&apos;, &apos;method.request.path.format&apos;],
      requestParameters: {
        &apos;integration.request.path.url&apos;: &apos;method.request.path.url&apos;,
        &apos;integration.request.path.format&apos;: &apos;method.request.path.format&apos;,
      },
    });
    endpoint.addMethod(&apos;GET&apos;, integration, {
      requestParameters: {
        &apos;method.request.path.url&apos;: true,
        &apos;method.request.path.format&apos;: true,
      },
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Tools to work with accessibility on the Web</title><link>https://kewah.com/tools-to-work-with-accessibility-on-the-web/</link><guid isPermaLink="true">https://kewah.com/tools-to-work-with-accessibility-on-the-web/</guid><description>The three tools I use on macOS for testing accessibility.
</description><pubDate>Wed, 08 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Accessibility is often viewed as making your site work on screen readers. In reality, web accessibility is a subset of User Experience (UX) focused on making your websites usable by the widest range of people possible, including those who have disabilities.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://a11yproject.com/posts/myth-accessibility-is-blind-people/&quot;&gt;https://a11yproject.com/posts/myth-accessibility-is-blind-people&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It&apos;s important to acknowledge that when we talk about accessibility, we don&apos;t only talk about making the website available for people with permanent disabilities, for instance, someone blind or deaf. But, we also include people with temporary disability. Think of someone with a broken arm or a parent with a child in his/her arms. And finally, there could be people in a situation of disability. Like when we have a glow on the screen because of the sunlight. Anyone can be affected in a moment in time by a disability that would prevent us from using a service.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;According to the new figures released by the Census Bureau on July 25, 2012, 56.7 million Americans (18.7% of the U.S. population) have some type of disability and out of this number, an estimated 38.3 million (12.6%) have a severe disability.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.interactiveaccessibility.com/accessibility-statistics&quot;&gt;https://www.interactiveaccessibility.com/accessibility-statistics&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Taking accessibility into account means &lt;strong&gt;increasing our target audience&lt;/strong&gt; by allowing as many people as possible to use our services.&lt;/p&gt;
&lt;p&gt;And finally, the number of lawsuits in the US has increased by &lt;a href=&quot;https://www.adatitleiii.com/2019/01/number-of-federal-website-accessibility-lawsuits-nearly-triple-exceeding-2250-in-2018/&quot;&gt;177%&lt;/a&gt; from 2017 to 2018, and we can expect it to continue on that trend. For instance, &lt;a href=&quot;https://www.cnbc.com/2019/10/07/dominos-supreme-court.html&quot;&gt;Dominos lost last year against a blind man named Guillermo Robles&lt;/a&gt;. So, by taking accessibility into account early in the process of development, we avoid having to hire more lawyers ;)&lt;/p&gt;
&lt;p&gt;With &lt;a href=&quot;https://acast.com&quot;&gt;Acast&lt;/a&gt; design system &lt;a href=&quot;https://medium.com/acast-tech/decibel-acasts-design-system-a705728ce2b0&quot;&gt;Decibel&lt;/a&gt;, we aim to have accessible components. It reduces the possibility of having non-accessible interfaces. Still, it doesn&apos;t entirely remove the need for the developer to test their product to make sure that everything fits well together. Therefore it&apos;s important to include accessibility into our workflow.&lt;/p&gt;
&lt;h2&gt;Accessibility tools&lt;/h2&gt;
&lt;p&gt;There are several tools available to help us when working on the client-side. It&apos;s easy to miss an Aria attribute, I&apos;ve done it myself many many times, so the tools I will introduce are here to give us some hints about what could be improved.&lt;/p&gt;
&lt;h3&gt;VoiceOver&lt;/h3&gt;
&lt;p&gt;VoiceOver is the screen reader for macOS. It helps us to validate that users can navigate between pages or if elements can easily be reached. People using mouses don&apos;t wait to read the full page to make a decision. The same goes for people using screen readers. They don&apos;t wait for the voice over to read the full page to decide what they want to do. I recommend checking Youtube videos from people using screen readers to see how effective they can be. They usually use the rotor to navigate between pages quickly.&lt;/p&gt;
&lt;p&gt;As a developer, it&apos;s essential to verify that the page architecture is correct, and all links or interactive elements are available in the rotor.&lt;/p&gt;
&lt;p&gt;To open the rotor, we need to press &lt;code&gt;cmd+f5&lt;/code&gt; to start VoiceOver, then &lt;code&gt;ctrl+opt+u&lt;/code&gt;. The, we can navigate between the section using arrow keys.&lt;/p&gt;
&lt;p&gt;For instance, it can help us catching if a button has a meaningful label. In the screenshot below, the &quot;button&quot; refers to the play button, so it&apos;s complicated for the user to know what the &quot;button&quot; will do.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Another example is buttons that are not implemented as such. It&apos;s common for developers to use &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s because it&apos;s quicker to style. But, if we forget to add Aria attributes to specify what the div is, the screen reader won&apos;t be able to find it. In the screenshot below, the &quot;sign up&quot; button can&apos;t be found in the &quot;Links.&quot; So people using screen readers or navigating with keyboards won&apos;t be able to reach for the button and join Acast.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;On a side note, VoiceOver works better when using Safari. So, it&apos;s recommended to use it while testing with VoiceOver.&lt;/p&gt;
&lt;h3&gt;Axe&lt;/h3&gt;
&lt;p&gt;Axe is an accessibility engine that is available through different types of plugins. They make sure that their accessibility tests have 0 false positives. So, they only test violations that they are confident about.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Devtool plugin (&lt;a href=&quot;https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/&quot;&gt;Firefox&lt;/a&gt; and &lt;a href=&quot;https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd&quot;&gt;Chrome&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Axe can be used as a devtool extension. When opening it in the devtool it will display the list of issues that can be fixed in the current page.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;React plugin (&lt;a href=&quot;https://www.npmjs.com/package/react-axe&quot;&gt;react-axe&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is also a React plugin that will display the report in the console. You can enable it in the entry point of your react app (where you mount the root component).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &apos;react&apos;;
import ReactDOM from &apos;react-dom&apos;;

if (process.env.NODE_ENV !== &apos;production&apos;) {
  const axe = require(&apos;react-axe&apos;);
  axe(React, ReactDOM, 1000);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cypress (&lt;a href=&quot;https://www.npmjs.com/package/cypress-axe&quot;&gt;cypress-axe&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Cypress plugin will inject Axe and check the DOM when navigating to a page. It will make your test fail when there is an a11y violation.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;describe(&apos;doc example&apos;, () =&amp;gt; {
  beforeEach(() =&amp;gt; {
    cy.visit(&apos;http://localhost:9000&apos;);
    cy.injectAxe();
  });

  it(&apos;Has no detectable a11y violations on load&apos;, () =&amp;gt; {
    cy.checkA11y();
  });

  it(&apos;Has no a11y violations after button click&apos;, () =&amp;gt; {
    cy.get(&apos;button&apos;).click();
    cy.checkA11y();
  });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Tota11y&lt;/h3&gt;
&lt;p&gt;Tota11y is an accessibility visualization toolkit developed by the Khan Academy. It has several overlaps with VoiceOver rotor and Axe, but I think it&apos;s an excellent complement. I find it useful to review a page for specific elements quickly. For instance, we can check the heading hierarchy and spot if we are misusing a heading level.&lt;/p&gt;
&lt;p&gt;The plugin is available for &lt;a href=&quot;https://addons.mozilla.org/en-US/firefox/addon/tota11y-accessibility-toolkit/&quot;&gt;Firefox&lt;/a&gt; and &lt;a href=&quot;https://chrome.google.com/webstore/detail/tota11y-plugin-from-khan/oedofneiplgibimfkccchnimiadcmhpe&quot;&gt;Chrome&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Or if an input doesn&apos;t have any label&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Aside from these tools, I also recommend using as much as possible &lt;a href=&quot;https://testing-library.com/docs/dom-testing-library/api-queries&quot;&gt;React Testing Library selectors&lt;/a&gt; to target attributes that the user sees. It&apos;s a good practice to avoid using &lt;code&gt;*ByTestId&lt;/code&gt; but instead rely on other selectors such as &lt;code&gt;*ByLabel&lt;/code&gt;, &lt;code&gt;*ByAltText&lt;/code&gt;, etc.&lt;/p&gt;
&lt;p&gt;So far, I haven&apos;t found a better tool than a screen reader to fully test the user experience. Please, don&apos;t hesitate to share the tools you are using to test accessibility.&lt;/p&gt;
</content:encoded></item><item><title>Decibel: Acast’s design system</title><link>https://kewah.com/decibel-acasts-design-system/</link><guid isPermaLink="true">https://kewah.com/decibel-acasts-design-system/</guid><description>Learn more about Acast&apos;s design system Decibel.
</description><pubDate>Wed, 11 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I wrote about the component library side of our design system at Acast. Including how we work with it as a remote first company and the tools we use.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/acast-tech/decibel-acasts-design-system-a705728ce2b0&quot;&gt;Read the article on Acast&apos;s tech blog&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can also watch my presentation at Sthlm.js meetup in which I talk about our journey to build Decibel.&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube-nocookie.com/embed/DCk-gjrJQmU?start=460&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;The slides can be downloaded &lt;a href=&quot;/assets/sthlmjs-meetup.pdf&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>How to keep small PRs</title><link>https://kewah.com/how-to-keep-small-pull-requests/</link><guid isPermaLink="true">https://kewah.com/how-to-keep-small-pull-requests/</guid><description>Walkthrough different Git commands to stack Pull Requests and keep them small.
</description><pubDate>Sun, 14 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Pull Requests (or PRs) are an essential part of team collaboration. It helps to catch issues, spread knowledge, and it brings confidence in our changes. When reviewing code, the reviewers also take responsibility for the changes that will be merged. So, when opening a PR, it&apos;s important to share as much information as possible. It helps the reviewers to recreate the mental model we had when working on the task (Why we did that change? How we did it? Why we did it that way). The key is to keep our PRs small and focused. It&apos;s fine to have a PR with several commits, and it&apos;s even an excellent way to share the flow of our thoughts and changes. But the changes have the be related to the same goal. When PRs are significant, it&apos;s harder to spot bugs, design flaws and the reviewers might feel overwhelmed. The worse case would be to approve the change without paying attention to it, which would defeat the initial purpose of working with PRs.&lt;/p&gt;
&lt;p&gt;In this article, we will go through different scenarios to split a PR into smaller chunks.&lt;/p&gt;
&lt;h2&gt;Scenario 1: clean history, but too many unrelated changes&lt;/h2&gt;
&lt;p&gt;Before opening our PR, we realize that we could move some commits into a different PR. For instance, we created a new helper function on the way, and it will help reviewers to pay more attention to it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git log
## branch: new-feature
* 8ee70d7 Commit 3
* b6a5da4 Add new helper
* d3b11d2 Commit 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will create a new branch from &lt;code&gt;master&lt;/code&gt; and reuse the commit &quot;Add new helper&quot; using &lt;code&gt;cherry-pick&lt;/code&gt;. And then create a PR for that new branch.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git log
## branch: new-feature

$ git checkout -b style-helper master
## branch: style-helper
$ git cherry-pick b6a5da4
$ git push origin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now go on GitHub and open the pull request.&lt;/p&gt;
&lt;p&gt;The last step is to push &lt;code&gt;new-feature&lt;/code&gt; to GitHub, but when we open the pull request, we need to &lt;strong&gt;change the base branch&lt;/strong&gt; and use &lt;code&gt;style-helper&lt;/code&gt; branch. (On GitLab it&apos;s called the &quot;target branch&quot;.) We can check the commit history on the GitHub interface and see that &quot;Add new helper&quot; is not part of it. After merging &lt;code&gt;style-helper&lt;/code&gt; PR, we should not forget to change the base branch to &lt;code&gt;master&lt;/code&gt; before merging &lt;code&gt;new-feature&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Scenario 2: need to split some commits&lt;/h2&gt;
&lt;p&gt;This time, we have mixed different changes in the same commit. We still want to create a different PR for our style helper, so we need to split that commit into multiple commits.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git log
## branch: new-feature
* 8ee70d7 Commit 2
* d3b11d2 Commit 1 &amp;lt;-- commit we want to split
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first step is to go back to &quot;Commit 1&quot; using &lt;code&gt;rebase&lt;/code&gt;, and then we will &lt;code&gt;reset&lt;/code&gt; that commit to split it using &lt;code&gt;add --patch&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;## branch: new-feature

git rebase -i d3b11d2^
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vim, or the editor defined in your gitconfig, will open and we specify which commit we want to &lt;code&gt;edit&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pick 8ee70d7 Commit 2
e d3b11d2 Commit 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are now back at &quot;Commit 1&quot; and it&apos;s now time to split it. We first need to &lt;code&gt;reset&lt;/code&gt; the commit to put the change back in an unstaged state so we can later use &lt;code&gt;add --patch&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git reset HEAD~
$ git add --patch
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We follow the prompt and decide which hunk we want to stage using yes, no or edit.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git commit -m &quot;Split 1&quot;
$ git add --patch
$ git commit -m &quot;Split 2: style helper&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are now done with splitting the commit. We can complete the &lt;code&gt;rebase&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git rebase --continue
$ git log
## branch: new-feature
* 8ee70d7 Commit 2
* 02dbeb3 Split 2: style helper
* cf4ca41 Split 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are at a similar stage as the first scenario, and we will go through the same steps for opening the two PRs.&lt;/p&gt;
&lt;h2&gt;Scenario 3: it&apos;s a mess, let&apos;s rewrite history&lt;/h2&gt;
&lt;p&gt;Sometimes things don&apos;t go according to plan, and we end up with a spaghetti Git history. The best, in that case, is to rewrite it.&lt;/p&gt;
&lt;p&gt;The first step is to find from which commit we want to start fresh.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git log
## branch: new-feature
* 255e8d5 Stuff 4
* 5c3fa7e Stuff 3
* deea3e8 Stuff 2
* 10a2b4a Stuff 1 &amp;lt;-- from here

$ git reset 10a2b4a^
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now everything is back to an unstaged state. We will commit the change we want to include in our first PR using &lt;code&gt;add --patch&lt;/code&gt;. Then &lt;code&gt;stash&lt;/code&gt; the rest to create a new branch and continue to commit from there and finally open our second PR.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;## branch: new-feature

$ git add --patch
$ git commit -m &quot;Commit 1&quot;
$ git stash
$ git push origin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can open the first PR using &lt;code&gt;master&lt;/code&gt; as base branch.&lt;/p&gt;
&lt;p&gt;Next, we create a new branch for the second PR.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;## branch: new-feature

$ git checkout -b pr-2
## branch: pr-2
$ git stash pop
$ git add --patch
$ git commit -m &quot;Commit 2&quot;
$ git stash
$ git push origin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can open the second PR using &lt;code&gt;new-feature&lt;/code&gt; as base branch.&lt;/p&gt;
&lt;p&gt;We repeat these steps until we don&apos;t have anything left in the stage.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;It might feel cumbersome to go through these steps, but I think it&apos;s important to show empathy to the reviewers and put them in good condition.&lt;/p&gt;
&lt;p&gt;These are the scenarios I have encountered to split a PR into smaller chunks, feel free to share a different way of doing.&lt;/p&gt;
</content:encoded></item><item><title>Why I use React Testing Library instead of Enzyme</title><link>https://kewah.com/testing-react-using-testing-library/</link><guid isPermaLink="true">https://kewah.com/testing-react-using-testing-library/</guid><description>The reasons I started to use react-testing-library for testing React components and why I like it.
</description><pubDate>Fri, 14 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://airbnb.io/enzyme/&quot;&gt;Enzyme&lt;/a&gt; has been my weapon of choice since 2016 for testing my React components. What I liked the most with Enzyme was the isolation of the component when testing it using shallow rendering. I could focus on testing the component behaviour and checking that the correct props were passed down to the children (mostly using snapshot testing). That allowed me not to have to mock the children components, for instance for root components, and have tests that run quickly. So, I was only testing at the unit level, each component separately, and tested the big picture using &lt;a href=&quot;https://www.cypress.io/&quot;&gt;Cypress&lt;/a&gt; (end to end testing).&lt;/p&gt;
&lt;p&gt;It’s only when I started to use React 16.8 (“The One With Hooks”) that I looked at other testing libraries. The main reason was the non-possibility to test custom hooks with Enzyme and not being able to use shallow rendering. Which led me to be more curious with other solutions and to dig into &lt;a href=&quot;https://testing-library.com/docs/react-testing-library/intro&quot;&gt;react-testing-library&lt;/a&gt; finally.&lt;/p&gt;
&lt;h2&gt;Issues with Enzyme&lt;/h2&gt;
&lt;p&gt;When rendering a component using Enzyme, it wraps the component and allows us to traverse the tree and access the component’s data (instance, props, state, children, …) using a rich API. But, great power comes great responsibility and being able to access internal state of the component (&lt;code&gt;.state()&lt;/code&gt;, &lt;code&gt;.setState()&lt;/code&gt;, &lt;code&gt;.instance()&lt;/code&gt;) often leads to test its implementation. I saw it many times when working on a React codebase, sometimes because it’s “easier and faster than having to reproduce the user steps”. For instance, in a component that has buttons I used to search for a &lt;code&gt;Button&lt;/code&gt; component and call its &lt;code&gt;onClick&lt;/code&gt; prop instead of having to mount and simulate the click on that element.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wrapper.find(&apos;Button&apos;).at(0).prop(&apos;onClick&apos;)();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But, the test would break if we replace the &lt;code&gt;Button&lt;/code&gt; with a component having similar functionalities or if we decide to move the button at a different index. It can get really complicated to work with this type of test, since every time you touch the implementation a test breaks. Which makes refactoring really painful. It’s not Enzyme faults per se, but giving access to the private properties to developers allow them to take shortcuts and test the implementation detail which results in brittle tests.&lt;/p&gt;
&lt;h2&gt;Different approach&lt;/h2&gt;
&lt;p&gt;React testing library has a different approach to testing than Enzyme. It’s closer to integration testing than unit testing. It renders the component and attaches it to the DOM. We only have access to the elements that are in the DOM. No internal state, no instance methods, just what the user can interact with. And that’s at the end the most important, we want to make sure that our code won’t break when it gets in the user’s hands. It helped me to rethink how and what I test when working on React components.&lt;/p&gt;
&lt;p&gt;So far, it&apos;s been a great experience:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I like that there is only one type of rendering, so I don&apos;t need to think about it;&lt;/li&gt;
&lt;li&gt;Tests are still fast to run. I was in the wrong impression that rendering the component on the DOM between each test would be more expensive;&lt;/li&gt;
&lt;li&gt;The DOM utils are focused on accessibility (&lt;code&gt;getByRole&lt;/code&gt;, &lt;code&gt;getByAltText&lt;/code&gt;, etc);&lt;/li&gt;
&lt;li&gt;I rely less on snapshot testing and test more individual elements (&lt;a href=&quot;https://github.com/testing-library/jest-dom&quot;&gt;jest-dom&lt;/a&gt; is handy for that) so tests break less when I change an unrelated node element;&lt;/li&gt;
&lt;li&gt;My test structure is very similar to what I used to do with Enzyme using a setup factory.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But, it also comes with some downside like having to mock children dependencies, for instance, if it fetches data on mount. So depending on the need, I might use &lt;code&gt;jest.mock&lt;/code&gt; to mock a child component.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { render } from &apos;@testing-library/react&apos;;
import React from &apos;react&apos;;
import App from &apos;./App&apos;;
import SomeAppSection from &apos;./SomeAppSection&apos;;

jest.mock(&apos;./SomeAppSection&apos;);

SomeAppSection.mockReturnValue(&amp;lt;div&amp;gt;SomeAppSection&amp;lt;/div&amp;gt;);

const setup = (props = {}) =&amp;gt; render(&amp;lt;App logout={jest.fn()} {...props} /&amp;gt;);

it(&apos;renders SomeAppSection&apos;, () =&amp;gt; {
  const logout = jest.fn();
  const { getByText } = setup({ logout });

  getByText(&apos;SomeAppSection&apos;);
  expect(SomeAppSection).toHaveBeenCalledWith({ logout }, {});
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, I don’t want to have to deal with &lt;code&gt;SomeAppSection&lt;/code&gt; behaviour since it will require me to have too many things to mock or keep track of when the component will change in the future. So, I fall back to what I used to do with Enzyme by mocking it away.&lt;/p&gt;
&lt;p&gt;Great reads related to testing-library or testing in general:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kentcdodds.com/blog/testing-implementation-details&quot;&gt;Testing Implementation Details&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kentcdodds.com/blog/how-to-know-what-to-test&quot;&gt;How to know what to test&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/javascript-scene/what-every-unit-test-needs-f6cd34d9836d&quot;&gt;5 Questions Every Unit Test Must Answer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kentcdodds.com/blog/avoid-the-test-user&quot;&gt;Avoid the Test User&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>React Hooks by example: useState, useCallback, useEffect, useReducer</title><link>https://kewah.com/react-hooks-by-example/</link><guid isPermaLink="true">https://kewah.com/react-hooks-by-example/</guid><description>Walk through the refactoring of a React class-based component to use useCallback, useEffect, useReducer and useState hooks.
</description><pubDate>Fri, 07 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In this article, we will touch upon how to use &lt;code&gt;useCallback&lt;/code&gt;, &lt;code&gt;useEffect&lt;/code&gt;, &lt;code&gt;useReducer&lt;/code&gt; and &lt;code&gt;useState&lt;/code&gt; hooks.&lt;/p&gt;
&lt;p&gt;We will build a component that gives the user the ability to search for a list of users. The component will store the data about the request state (if it’s loading) and response (the user list or the error information). It will listen for the form submit event and call the backend with the input’s value to get the list of users. There are different ways to achieve it, such as using Redux, but we will keep it basic since we will focus on the hooks.&lt;/p&gt;
&lt;h2&gt;The class way (without hooks)&lt;/h2&gt;
&lt;p&gt;Using a class component, it could look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class UserSearch extends React.Component {
  constructor(props, ...rest) {
    super(props, ...rest);

    this.state = {
      loading: false,
      error: undefined,
      users: undefined,
    };
  }

  componentWillUnmount() {
    if (this.request) {
      this.request.abort();
    }
  }

  handleFormSubmit = (event) =&amp;gt; {
    this.setState({ loading: true });

    this.request = superagent.get(`http://localhost:8080/users/${event.target.elements.username.value}`);
    this.request
      .then((response) =&amp;gt; {
        this.setState({
          loading: false,
          users: response.body.items,
        });
      })
      .catch((error) =&amp;gt; {
        this.setState({
          loading: false,
          error,
        });
      });
  };

  render() {
    const { loading, error, users, searchValue } = this.state;

    return (
      &amp;lt;form onSubmit={this.handleFormSubmit}&amp;gt;
        {error &amp;amp;&amp;amp; &amp;lt;p&amp;gt;Error: {error.message}&amp;lt;/p&amp;gt;}

        &amp;lt;input type=&quot;text&quot; name=&quot;username&quot; disabled={loading} /&amp;gt;
        &amp;lt;button type=&quot;submit&quot; disabled={loading}&amp;gt;
          Search
        &amp;lt;/button&amp;gt;

        {loading &amp;amp;&amp;amp; &amp;lt;p&amp;gt;Loading...&amp;lt;/p&amp;gt;}

        {users &amp;amp;&amp;amp; (
          &amp;lt;div&amp;gt;
            &amp;lt;h1&amp;gt;Result&amp;lt;/h1&amp;gt;
            &amp;lt;ul&amp;gt;
              {users.map(({ id, name }) =&amp;gt; (
                &amp;lt;li key={id}&amp;gt;{name}&amp;lt;/li&amp;gt;
              ))}
            &amp;lt;/ul&amp;gt;
          &amp;lt;/div&amp;gt;
        )}
      &amp;lt;/form&amp;gt;
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The functional way&lt;/h2&gt;
&lt;p&gt;We will refactor the &lt;code&gt;UserSearch&lt;/code&gt; component step by step and introduce the hooks on the way.&lt;/p&gt;
&lt;p&gt;We no longer need to use classes when we use hooks. The first step is to extract the render method into a function based component. We also inline the state and the event handlers, but currently, they don’t do anything.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const UserSearch = () =&amp;gt; {
  const loading = false;
  const users = undefined;
  const error = undefined;

  const handleFormSubmit = () =&amp;gt; {
    // TODO
  };

  return (
    &amp;lt;form onSubmit={handleFormSubmit}&amp;gt;
      {error &amp;amp;&amp;amp; &amp;lt;p&amp;gt;Error: {error.message}&amp;lt;/p&amp;gt;}

      &amp;lt;input type=&quot;text&quot; name=&quot;username&quot; disabled={loading} /&amp;gt;
      &amp;lt;button type=&quot;submit&quot; disabled={loading}&amp;gt;
        Search
      &amp;lt;/button&amp;gt;

      {loading &amp;amp;&amp;amp; &amp;lt;p&amp;gt;Loading...&amp;lt;/p&amp;gt;}

      {users &amp;amp;&amp;amp; (
        &amp;lt;div&amp;gt;
          &amp;lt;h1&amp;gt;Result&amp;lt;/h1&amp;gt;
          &amp;lt;ul&amp;gt;
            {users.map(({ id, name }) =&amp;gt; (
              &amp;lt;li key={id}&amp;gt;{name}&amp;lt;/li&amp;gt;
            ))}
          &amp;lt;/ul&amp;gt;
        &amp;lt;/div&amp;gt;
      )}
    &amp;lt;/form&amp;gt;
  );
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Introducing hooks&lt;/h2&gt;
&lt;h2&gt;useState&lt;/h2&gt;
&lt;p&gt;We can use the &lt;code&gt;useState&lt;/code&gt; hook to store the different states we have in our component (loading, users, error). &lt;code&gt;useState&lt;/code&gt; takes the initial value as a parameter and returns a tuple of the state value and a function to update the value.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const [value, setValue] = useState(initialValue);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s update our states using &lt;code&gt;setState&lt;/code&gt;. Currently, we only initialize the
states, but we need to implement the logic.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const UserSearch = () =&amp;gt; {
  // highlight-start
  const [loading, setLoading] = userState(false);
  const [users, setUsers] = useState();
  const [error, setError] = useState();
  // highlight-end

  const handleFormSubmit = () =&amp;gt; {
    // TODO
  };

  return (
    &amp;lt;form onSubmit={handleFormSubmit}&amp;gt;
      {error &amp;amp;&amp;amp; &amp;lt;p&amp;gt;Error: {error.message}&amp;lt;/p&amp;gt;}

      &amp;lt;input type=&quot;text&quot; name=&quot;username&quot; disabled={loading} /&amp;gt;
      &amp;lt;button type=&quot;submit&quot; disabled={loading}&amp;gt;
        Search
      &amp;lt;/button&amp;gt;

      {loading &amp;amp;&amp;amp; &amp;lt;p&amp;gt;Loading...&amp;lt;/p&amp;gt;}

      {users &amp;amp;&amp;amp; (
        &amp;lt;div&amp;gt;
          &amp;lt;h1&amp;gt;Result&amp;lt;/h1&amp;gt;
          &amp;lt;ul&amp;gt;
            {users.map(({ id, name }) =&amp;gt; (
              &amp;lt;li key={id}&amp;gt;{name}&amp;lt;/li&amp;gt;
            ))}
          &amp;lt;/ul&amp;gt;
        &amp;lt;/div&amp;gt;
      )}
    &amp;lt;/form&amp;gt;
  );
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;useCallback&lt;/h2&gt;
&lt;p&gt;A function-based component doesn’t have lifecycles and React calls the function for each new render, which means that for each re-render every hoisted object will be recreated. For instance, a new &lt;code&gt;handleFormSubmit&lt;/code&gt; function is created every time. One of the issues is that it invalidates the tree because &lt;code&gt;&amp;lt;form onSubmit={handleFormSubmit}&amp;gt;&lt;/code&gt; is different between renders (previous &lt;code&gt;handleFormSubmit&lt;/code&gt; ≠ next &lt;code&gt;handleFormSubmit&lt;/code&gt; because &lt;code&gt;() =&amp;gt; {} !== () =&amp;gt; {}&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;That’s where &lt;code&gt;useCallback&lt;/code&gt; comes into play. It caches the function and creates a new one only if a dependency changes. A dependency is a value that is created in the component but is outside the &lt;code&gt;useCallback&lt;/code&gt; scope.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const fn = useCallback(() =&amp;gt; {}, [dependencies]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the &lt;a href=&quot;https://reactjs.org/docs/hooks-reference.html#usecallback&quot;&gt;documentation&lt;/a&gt;, they recommend “every value referenced inside the callback should also appear in the dependencies array.” Although, you may omit &lt;code&gt;dispatch&lt;/code&gt; (from &lt;code&gt;useReducer&lt;/code&gt;), &lt;code&gt;setState&lt;/code&gt;, and &lt;code&gt;useRef&lt;/code&gt; container values from the dependencies because React guarantees them to be static. However, it doesn’t hurt to specify them. Note that If we pass an empty array for the dependencies, it will always return the same function.&lt;/p&gt;
&lt;p&gt;I recommend you to use &lt;a href=&quot;https://www.npmjs.com/package/eslint-plugin-react-hooks&quot;&gt;eslint-plugin-react-hooks&lt;/a&gt; to help you to know which values we need to include in the dependencies.&lt;/p&gt;
&lt;p&gt;You should also check the article written by Kent C. Dodds about &lt;a href=&quot;https://kentcdodds.com/blog/usememo-and-usecallback&quot;&gt;when to use &lt;code&gt;useCallback&lt;/code&gt;&lt;/a&gt; since it also comes with a performance cost to use it over an inline callback. Spoiler: for referential equality and dependencies lists.&lt;/p&gt;
&lt;p&gt;So, if we follow how it was done with the class, we could execute the &lt;code&gt;GET&lt;/code&gt; request directly in the &lt;code&gt;useCallback&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const UserSearch = () =&amp;gt; {
  const [loading, setLoading] = userState(false);
  const [users, setUsers] = useState();
  const [error, setError] = useState();

  // highlight-start
  const handleFormSubmit = useCallback(
    (event) =&amp;gt; {
      event.preventDefault();

      setLoading(true);

      const request = superagent.get(`http://localhost:8080/users/${event.target.elements.username.value}`);
      request
        .then((response) =&amp;gt; {
          setLoading(false);
          setUsers(response.body.items);
        })
        .catch((error) =&amp;gt; {
          setLoading(false);
          setError(error);
        });
    },
    [setLoading, setUsers, setError]
  );
  // highlight-end

  return (
    &amp;lt;form onSubmit={handleFormSubmit}&amp;gt;
      {error &amp;amp;&amp;amp; &amp;lt;p&amp;gt;Error: {error.message}&amp;lt;/p&amp;gt;}

      &amp;lt;input type=&quot;text&quot; name=&quot;username&quot; disabled={loading} /&amp;gt;
      &amp;lt;button type=&quot;submit&quot; disabled={loading}&amp;gt;
        Search
      &amp;lt;/button&amp;gt;

      {loading &amp;amp;&amp;amp; &amp;lt;p&amp;gt;Loading...&amp;lt;/p&amp;gt;}

      {users &amp;amp;&amp;amp; (
        &amp;lt;div&amp;gt;
          &amp;lt;h1&amp;gt;Result&amp;lt;/h1&amp;gt;
          &amp;lt;ul&amp;gt;
            {users.map(({ id, name }) =&amp;gt; (
              &amp;lt;li key={id}&amp;gt;{name}&amp;lt;/li&amp;gt;
            ))}
          &amp;lt;/ul&amp;gt;
        &amp;lt;/div&amp;gt;
      )}
    &amp;lt;/form&amp;gt;
  );
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;⚠️ It works, there are few issues by doing that. When React unmounts the component, nothing aborts the request the same way we did in &lt;code&gt;componentWillUnmount&lt;/code&gt;. Also, since the request is pending React keeps a reference to an unmounted component. So, it wastes browser resources for something the user will never interact with.&lt;/p&gt;
&lt;h2&gt;useEffect&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;useEffect&lt;/code&gt; brings the lifecycle to a function based component. It is the combination of &lt;code&gt;componentDidMount&lt;/code&gt;, &lt;code&gt;componentDidUpdate&lt;/code&gt;, and &lt;code&gt;componentWillUnmount&lt;/code&gt;. The callback of &lt;code&gt;useEffect&lt;/code&gt; is executed when a dependency is updated. So, the first time the component is rendered, &lt;code&gt;useEffect&lt;/code&gt; will be executed. In our case, we want to start the request when the search value is updated (on form submit). We will introduce a new state &lt;code&gt;searchValue&lt;/code&gt; that is updated in the &lt;code&gt;handleFormSubmit&lt;/code&gt; handler and we will use that state as a dependency to the hook. Therefore when &lt;code&gt;searchValue&lt;/code&gt; is updated the &lt;code&gt;useEffect&lt;/code&gt; hook will also be executed.&lt;/p&gt;
&lt;p&gt;Finally, the &lt;code&gt;useEffect&lt;/code&gt; callback must return a function that is used to clean up, for us this is where we will abort the request.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const UserSearch = () =&amp;gt; {
  const [loading, setLoading] = userState(false);
  const [users, setUsers] = useState();
  const [error, setError] = useState();
  const [searchValue, setSearchValue] = useState();

  const handleFormSubmit = useCallback(
    (event) =&amp;gt; {
      event.preventDefault();
      // highlight-start
      setSearchValue(event.target.elements.username.value);
      // highlight-end
    },
    [setSearchValue]
  );

  // highlight-start
  useEffect(() =&amp;gt; {
    let request;

    if (searchValue) {
      setLoading(true);

      request = superagent.get(`http://localhost:8080/users/${event.target.elements.username.value}`);
      request
        .then((response) =&amp;gt; {
          setError(undefined);
          setLoading(false);
          setUsers(response.body.items);
        })
        .catch((error) =&amp;gt; {
          setLoading(false);
          setError(error);
        });
    }

    return () =&amp;gt; {
      if (request) {
        request.abort();
      }
    };
  }, [searchValue, setLoading, setUsers, setError]);
  // highlight-end

  return (
    &amp;lt;form onSubmit={handleFormSubmit}&amp;gt;
      {error &amp;amp;&amp;amp; &amp;lt;p&amp;gt;Error: {error.message}&amp;lt;/p&amp;gt;}

      &amp;lt;input type=&quot;text&quot; name=&quot;username&quot; disabled={loading} /&amp;gt;
      &amp;lt;button type=&quot;submit&quot; disabled={loading}&amp;gt;
        Search
      &amp;lt;/button&amp;gt;

      {loading &amp;amp;&amp;amp; &amp;lt;p&amp;gt;Loading...&amp;lt;/p&amp;gt;}

      {users &amp;amp;&amp;amp; (
        &amp;lt;div&amp;gt;
          &amp;lt;h1&amp;gt;Result&amp;lt;/h1&amp;gt;
          &amp;lt;ul&amp;gt;
            {users.map(({ id, name }) =&amp;gt; (
              &amp;lt;li key={id}&amp;gt;{name}&amp;lt;/li&amp;gt;
            ))}
          &amp;lt;/ul&amp;gt;
        &amp;lt;/div&amp;gt;
      )}
    &amp;lt;/form&amp;gt;
  );
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dan Abramov has written an excellent blog post about &lt;code&gt;useEffect&lt;/code&gt; hooks: &lt;a href=&quot;https://overreacted.io/a-complete-guide-to-useeffect/&quot;&gt;a complete guide to useEffect&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;useReducer&lt;/h2&gt;
&lt;p&gt;We have now a working version of our component using React Hooks 🎉. One thing we could improve is when we have to keep track of several states, such as in the request&apos;s response we update three states. In our example, I think it’s fine to go with the current version. However, in the case we need to add more states, &lt;code&gt;useReducer&lt;/code&gt; would be a better suit. That allows us to gather related states in the same area of our code and have one way to update the states.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;useReducer&lt;/code&gt; expects a reducer function (that function takes an action and returns a new state) and the initial state. Similar to &lt;code&gt;useState&lt;/code&gt; it returns a tuple that contains the state and the dispatch function that we use to dispatch actions.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const [state, dispatch] = useReducer(reducer, initialState);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// highlight-start
const initialState = {
  loading: false,
  users: undefined,
  error: undefined,
  searchValue: undefined,
};

const SET_SEARCH_VALUE = &apos;SET_SEARCH_VALUE&apos;;
const FETCH_INIT = &apos;FETCH_INIT&apos;;
const FETCH_SUCCESS = &apos;FETCH_SUCCESS&apos;;
const ERROR = &apos;ERROR&apos;;

const reducer = (state, { type, payload }) =&amp;gt; {
  switch (type) {
    case SET_SEARCH_VALUE:
      return {
        ...state,
        searchValue: payload,
      };

    case FETCH_INIT:
      return {
        ...state,
        error: undefined,
        loading: true,
      };

    case FETCH_SUCCESS:
      return {
        ...state,
        loading: false,
        error: undefined,
        result: payload,
      };

    case ERROR:
      return {
        ...state,
        loading: false,
        error: payload,
      };

    default:
      throw new Error(`Action type ${type} unknown`);
  }
};
// highlight-end

const UserSearch = () =&amp;gt; {
  const [state, dispatch] = useReducer(reducer, initialState);

  const handleFormSubmit = useCallback(
    (event) =&amp;gt; {
      event.preventDefault();

      // highlight-start
      dispatch({
        type: SET_SEARCH_VALUE,
        payload: event.target.elements.username.value,
      });
      // highlight-end
    },
    [dispatch]
  );

  useEffect(() =&amp;gt; {
    let request;

    if (state.searchValue) {
      // highlight-next-line
      dispatch({ type: FETCH_INIT });

      request = superagent.get(`http://localhost:8080/users/${state.searchValue}`);
      request
        .then((response) =&amp;gt; {
          // highlight-next-line
          dispatch({ type: FETCH_SUCCESS, payload: response.body.items });
        })
        .catch((error) =&amp;gt; {
          // highlight-next-line
          dispatch({ type: ERROR, payload: error });
        });
    }

    return () =&amp;gt; {
      if (request) {
        request.abort();
      }
    };
  }, [state.searchValue, dispatch]);

  return (
    &amp;lt;form onSubmit={handleFormSubmit}&amp;gt;
      {/* highlight-start */}
      {state.error &amp;amp;&amp;amp; &amp;lt;p&amp;gt;Error: {state.error.message}&amp;lt;/p&amp;gt;}

      &amp;lt;input type=&quot;text&quot; name=&quot;username&quot; disabled={state.loading} /&amp;gt;
      &amp;lt;button type=&quot;submit&quot; disabled={state.loading}&amp;gt;
        Search
      &amp;lt;/button&amp;gt;

      {state.loading &amp;amp;&amp;amp; &amp;lt;p&amp;gt;Loading...&amp;lt;/p&amp;gt;}

      {state.users &amp;amp;&amp;amp; (
        &amp;lt;div&amp;gt;
          &amp;lt;h1&amp;gt;Result&amp;lt;/h1&amp;gt;
          &amp;lt;ul&amp;gt;
            {state.users.map(({ id, name }) =&amp;gt; (
              &amp;lt;li key={id}&amp;gt;{name}&amp;lt;/li&amp;gt;
            ))}
          &amp;lt;/ul&amp;gt;
        &amp;lt;/div&amp;gt;
      )}
      {/* highlight-end */}
    &amp;lt;/form&amp;gt;
  );
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As mentioned before, the benefits are not directly apparent since we don&apos;t have that many states to handle in our example. There is more boilerplate than the &lt;code&gt;useState&lt;/code&gt; version, but all states related to calling the API are managed in the reducer function.&lt;/p&gt;
</content:encoded></item><item><title>Continuous deployment of a Phoenix project using GitLab CI/CD</title><link>https://kewah.com/gitlab-ci-for-elixir-phoenix-project/</link><guid isPermaLink="true">https://kewah.com/gitlab-ci-for-elixir-phoenix-project/</guid><description>Walk through the config to setup continuous integration and deployment using GitLab for a Phoenix project.
</description><pubDate>Sun, 02 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In this article, we will walk through the setup of &lt;a href=&quot;https://docs.gitlab.com/ee/ci/&quot;&gt;GitLab CI/CD&lt;/a&gt; pipelines to run the tests and deploy the latest version for each merge on master. We assume that you already have a &lt;a href=&quot;https://phoenixframework.org/&quot;&gt;Phoenix&lt;/a&gt; project up and running, and created a &lt;a href=&quot;https://gitlab.com&quot;&gt;GitLab repository&lt;/a&gt; for your project.&lt;/p&gt;
&lt;p&gt;We need to create the &lt;code&gt;gitlab-ci.yml&lt;/code&gt; file at the root of the repository. In this file, we will define the different jobs that are required to deploy a new version to production automatically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;init: we download the dependencies and build the project;&lt;/li&gt;
&lt;li&gt;lint: we make sure that the code follows our style guide;&lt;/li&gt;
&lt;li&gt;test: we make nothing has broken with our changes;&lt;/li&gt;
&lt;li&gt;deploy: push the new version to production. This step is only executed when we merge a feature branch on master.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;TLDR;&lt;/strong&gt; jump at the end of the article to have the full &lt;code&gt;gitlab-ci.yml&lt;/code&gt; config file.&lt;/p&gt;
&lt;h2&gt;Default settings&lt;/h2&gt;
&lt;p&gt;I the &lt;code&gt;gitlab-ci.yml&lt;/code&gt; file we define:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the list of stages;&lt;/li&gt;
&lt;li&gt;the env variables that are used by Phoenix for the database;&lt;/li&gt;
&lt;li&gt;the default settings for Elixir and JavaScript we will re-use for each jobs.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;stages:
  - init
  - lint
  - test
  - deploy

variables:
  POSTGRES_DB: test_test
  POSTGRES_HOST: postgres
  POSTGRES_USER: postgres
  POSTGRES_PASSWORD: &apos;postgres&apos;
  MIX_ENV: &apos;test&apos;

.elixir_default: &amp;amp;elixir_default
  image: elixir:1.8
  before_script:
    - mix local.hex --force
    - mix local.rebar --force

.javascript_default: &amp;amp;javascript_default
  image: node:alpine
  before_script:
    - cd assets
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Init stage&lt;/h2&gt;
&lt;p&gt;In the init stage, we set up the environment for the next jobs by compiling the project, downloading the dependencies and saving the artifacts.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;elixir_compile:
  &amp;lt;&amp;lt;: *elixir_default
  stage: init
  script:
    - apt-get update
    - apt-get install -y postgresql-client
    - mix deps.get --only test
    - mix compile --warnings-as-errors
  artifacts:
    paths:
      - mix.lock
      - _build
      - deps

javascript_deps:
  &amp;lt;&amp;lt;: *javascript_default
  stage: init
  script:
    - npm install --progress=false
  artifacts:
    paths:
      - assets/node_modules
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Lint stage&lt;/h2&gt;
&lt;h3&gt;Credo for Elixir&lt;/h3&gt;
&lt;p&gt;We use &lt;a href=&quot;https://github.com/rrrene/credo&quot;&gt;Credo&lt;/a&gt; to lint the Elixir part of the project. To install it we add its dependency to &lt;code&gt;mix.exs&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;defp deps do
  [
    {:credo, &quot;~&amp;gt; 1.0.0&quot;, only: [:dev, :test], runtime: false}
  ]
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are plenty of &lt;a href=&quot;https://github.com/rrrene/credo#configuration&quot;&gt;settings&lt;/a&gt; that are available with Credo to match your style guide.&lt;/p&gt;
&lt;h3&gt;Eslint and Prettier for JavaScript&lt;/h3&gt;
&lt;p&gt;For JavaScript, we use &lt;a href=&quot;https://blog.kewah.com/2018/eslint-and-prettier/&quot;&gt;Eslint + Prettier&lt;/a&gt;. We add the required dependencies that are automatically added in &lt;code&gt;assets/package.json&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd assets &amp;amp;&amp;amp; npm install --save-dev babel-eslint eslint eslint-config-prettier eslint-plugin-prettier prettier
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We define the &lt;a href=&quot;https://eslint.org/docs/user-guide/configuring&quot;&gt;Eslint&lt;/a&gt; config in &lt;code&gt;assets/.eslintrc&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;parser&quot;: &quot;babel-eslint&quot;,
  &quot;extends&quot;: [&quot;eslint:recommended&quot;, &quot;prettier&quot;],
  &quot;plugins&quot;: [&quot;prettier&quot;],
  &quot;rules&quot;: {
    &quot;prettier/prettier&quot;: &quot;error&quot;
  },
  &quot;env&quot;: {
    &quot;browser&quot;: true,
    &quot;jest&quot;: true,
    &quot;node&quot;: true
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the Prettier config in &lt;code&gt;assets/.prettierrc&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;trailingComma&quot;: &quot;es5&quot;,
  &quot;tabWidth&quot;: 2,
  &quot;semi&quot;: false,
  &quot;singleQuote&quot;: false
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we update &lt;code&gt;assets/package.json&lt;/code&gt; to add the lint task.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;scripts&quot;: {
  &quot;deploy&quot;: &quot;webpack --mode production&quot;,
  &quot;watch&quot;: &quot;webpack --mode development --watch&quot;,
  &quot;lint&quot;: &quot;eslint . --max-warnings=0&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;CI jobs&lt;/h3&gt;
&lt;p&gt;We can now add the jobs in &lt;code&gt;gitlab-ci.yml&lt;/code&gt; to run the linters in the CI.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;elixir_lint:
  &amp;lt;&amp;lt;: *elixir_default
  stage: lint
  script:
    - mix format --check-formatted
    - mix credo --strict

javascript_lint:
  &amp;lt;&amp;lt;: *javascript_default
  stage: lint
  script:
    - npm run lint
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Test&lt;/h2&gt;
&lt;h3&gt;Jest for JavaScript&lt;/h3&gt;
&lt;p&gt;We will use the &lt;code&gt;test&lt;/code&gt; task in our &lt;code&gt;assets/package.json&lt;/code&gt; to execute the client tests. I recommend using &lt;a href=&quot;https://jestjs.io/&quot;&gt;Jest&lt;/a&gt;. It&apos;s a fast test framework, has an easy way to mock dependencies, has a nice interactive CLI, and it comes with &lt;a href=&quot;https://github.com/jsdom/jsdom&quot;&gt;jsdom&lt;/a&gt; by default.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd assets &amp;amp;&amp;amp; npm install --save-dev jest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can setup Jest config by running &lt;code&gt;npx jest --init&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We run Jest in the test task by adding it to the &lt;code&gt;assets/package.json&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;scripts&quot;: {
  ...
  &quot;test&quot;: &quot;jest&quot;
},
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;CI jobs&lt;/h3&gt;
&lt;p&gt;Let&apos;s add the test jobs in our &lt;code&gt;gitlab-ci.yml&lt;/code&gt; file&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;elixir_test:
  &amp;lt;&amp;lt;: *elixir_default
  stage: test
  services:
    - postgres:11.3
  script:
    - mix ecto.create
    - mix ecto.migrate
    - mix test

javascript_test:
  &amp;lt;&amp;lt;: *javascript_default
  stage: test
  script:
    - npm run test
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Deploy&lt;/h2&gt;
&lt;p&gt;We will use &lt;a href=&quot;https://www.gigalixir.com/&quot;&gt;Gigalixir&lt;/a&gt; as a hosting platform. It&apos;s a PaaS designed for Elixir and Phoenix. The deployment is similar to &lt;a href=&quot;https://www.heroku.com/&quot;&gt;Heroku&lt;/a&gt;, where we deploy by pushing to a remote branch. Moreover, it has a free plan that allows us to run our app with a Postgres database.&lt;/p&gt;
&lt;p&gt;Before adding the deployment config for GitLab, we need to make sure everything is set up for Gigalixir locally.&lt;/p&gt;
&lt;p&gt;First we need to install the Gigalixir command line tools:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install gigalixir --ignore-installed six
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then login:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gigalixir login
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now create a new app with a Postgres database on their platform:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gigalixir create -n my-app
gigalixir pg:create --free
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Gigalixir has a good &lt;a href=&quot;https://gigalixir.readthedocs.io/en/latest/main.html#configuration-and-secrets&quot;&gt;documentation&lt;/a&gt; to config the production environment of our Phoenix project.&lt;/p&gt;
&lt;p&gt;We need to delete &lt;code&gt;prod.secret.exs&lt;/code&gt; file since Gigalixir is handling it for us and we will use the environment variable instead. Then we open &lt;code&gt;prod.exs&lt;/code&gt; and delete the import of &lt;code&gt;import_config &quot;prod.secret.exs&quot;&lt;/code&gt;. And finally, we add the configuration to connect to Gigalixir.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;config :my_app, MyAppWeb.Endpoint,
  http: [port: {:system, &quot;PORT&quot;}],
  url: [host: System.get_env(&quot;APP_NAME&quot;) &amp;lt;&amp;gt; &quot;.gigalixirapp.com&quot;, port: 80],
  secret_key_base: Map.fetch!(System.get_env(), &quot;SECRET_KEY_BASE&quot;),
  server: true

config :my_app, MyApp.Repo,
  adapter: Ecto.Adapters.Postgres,
  url: System.get_env(&quot;DATABASE_URL&quot;),
  ssl: true,
  pool_size: 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The environment variables are automatically set by Gigalixir when we deploy.&lt;/p&gt;
&lt;h3&gt;Specify versions&lt;/h3&gt;
&lt;p&gt;Next, we need to specify the versions we are using to build the project. At the root of the repository, we create the files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;elixir_buildpack.config&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;elixir_version=1.8.2
erlang_version=21.2.5
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;phoenix_static_buildpack.config&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;node_version=10.16.0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Build client side&lt;/h3&gt;
&lt;p&gt;We need to add a new file at the root of the repository called &lt;code&gt;compile&lt;/code&gt; and paste the following lines:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm run deploy
cd $phoenix_dir
mix &quot;${phoenix_ex}.digest&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are now ready to deploy. Commit the changes and manually push the first version to Gigalixir to make sure everything works as expected.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git push gigalixir master
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can navigate to the link of the app to check or check the logs by running
&lt;code&gt;gigalixir logs&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;CI job&lt;/h3&gt;
&lt;p&gt;We are now ready to add the deploy step in our &lt;code&gt;gitlab-ci.yml&lt;/code&gt; file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;deploy:
  stage: deploy
  script:
    - git remote add gigalixir $GIGALIXIR_REMOTE_URL
    - git push -f gigalixir HEAD:refs/heads/master
  when: manual
  only:
    - master
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that you can remove the line &lt;a href=&quot;https://docs.gitlab.com/ee/ci/yaml/#whenmanual&quot;&gt;&lt;code&gt;when: manual&lt;/code&gt;&lt;/a&gt; if you want to deploy a new version to production every time you merge to master. Otherwise, with &lt;code&gt;when: manual&lt;/code&gt; you will need to go on GitLab pipeline interface and start this step manually.&lt;/p&gt;
&lt;p&gt;We added a new environment variable &lt;code&gt;$GIGALIXIR_REMOTE_URL&lt;/code&gt;. You need to set its value in GitLab project&apos;s settings (settings → CI/CD → variables). It should have the following format:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://name%40mail-provider.com:password@git.gigalixir.com/my-project-name.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can find the values by opening &lt;code&gt;~/.netrc&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;machine git.gigalixir.com
  login doe@fastmail.com
  password 1234-5678-9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, for our &lt;code&gt;my-app&lt;/code&gt; project the remove URL is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://doe%40fastmail.com:1234-5678-9@git.gigalixir.com/my-app.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&apos;s merge our changes on master and watch GitLab deploying our app on Gigalixir 🎉&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;The all together gitlab-ci.yml&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;variables:
  POSTGRES_DB: test_test
  POSTGRES_HOST: postgres
  POSTGRES_USER: postgres
  POSTGRES_PASSWORD: &apos;postgres&apos;
  MIX_ENV: &apos;test&apos;

stages:
  - init
  - lint
  - test
  - deploy

.elixir_default: &amp;amp;elixir_default
  image: elixir:1.8
  before_script:
    - mix local.hex --force
    - mix local.rebar --force

.javascript_default: &amp;amp;javascript_default
  image: node:alpine
  before_script:
    - cd assets

elixir_compile:
  &amp;lt;&amp;lt;: *elixir_default
  stage: init
  script:
    - apt-get update
    - apt-get install -y postgresql-client
    - mix deps.get --only test
    - mix compile --warnings-as-errors
  artifacts:
    paths:
      - mix.lock
      - _build
      - deps

elixir_lint:
  &amp;lt;&amp;lt;: *elixir_default
  stage: lint
  script:
    - mix format --check-formatted
    - mix credo --strict

elixir_test:
  &amp;lt;&amp;lt;: *elixir_default
  stage: test
  services:
    - postgres:11.3
  script:
    - mix ecto.create
    - mix ecto.migrate
    - mix test

javascript_deps:
  &amp;lt;&amp;lt;: *javascript_default
  stage: init
  script:
    - npm install --progress=false
  artifacts:
    paths:
      - assets/node_modules

javascript_lint:
  &amp;lt;&amp;lt;: *javascript_default
  stage: lint
  script:
    - npm run lint

javascript_test:
  &amp;lt;&amp;lt;: *javascript_default
  stage: test
  script:
    - npm run test

deploy:
  stage: deploy
  script:
    - git remote add gigalixir $GIGALIXIR_REMOTE_URL
    - git push -f gigalixir HEAD:refs/heads/master
  when: manual
  only:
    - master
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Writing a meaningful commit message</title><link>https://kewah.com/git-commit-message/</link><guid isPermaLink="true">https://kewah.com/git-commit-message/</guid><description>Practices to write a meaningful commit message.
</description><pubDate>Fri, 16 Nov 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When working in a team of developers, it&apos;s important to share what you attended to achieve with your code and why you did it that way. It can be done using clear variable names, comments, ... but also via Git commit messages.&lt;/p&gt;
&lt;p&gt;Commit messages are useful for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Code review: it helps the reviewer to follow your chain of thoughts, understand what you solved in your PR.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git blame&lt;/code&gt;: it brings context to the line/block of code.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git log&lt;/code&gt;, &lt;code&gt;git rebase&lt;/code&gt;, &lt;code&gt;git revert&lt;/code&gt; or &lt;code&gt;git bisect&lt;/code&gt;: it helps you to grasp what the changes are about without having to dig into each line of code contained in the commit.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The structure of a commit message&lt;/h2&gt;
&lt;p&gt;I follow the rules detailed in the famous &lt;a href=&quot;https://chris.beams.io/posts/git-commit/#seven-rules&quot;&gt;article&lt;/a&gt; from Chris Beams. They are the most common I encountered at work or in open source projects.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://chris.beams.io/posts/git-commit/#separate&quot;&gt;Separate subject from body with a blank line&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chris.beams.io/posts/git-commit/#limit-50&quot;&gt;Limit the subject line to 50 characters&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chris.beams.io/posts/git-commit/#capitalize&quot;&gt;Capitalize the subject line&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chris.beams.io/posts/git-commit/#end&quot;&gt;Do not end the subject line with a period&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chris.beams.io/posts/git-commit/#imperative&quot;&gt;Use the imperative mood in the subject line&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chris.beams.io/posts/git-commit/#wrap-72&quot;&gt;Wrap the body at 72 characters&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chris.beams.io/posts/git-commit/#why-not-how&quot;&gt;Use the body to explain &lt;em&gt;what&lt;/em&gt; and &lt;em&gt;why&lt;/em&gt; vs. &lt;em&gt;how&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I like to follow his methodology to write the subject line. When writing the message, I think about the following sentence:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;If applied, this commit will [your subject line]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I strongly recommend you to read the full article &lt;a href=&quot;https://chris.beams.io/posts/git-commit&quot;&gt;How to Write a Git Commit Message&lt;/a&gt;, it has a lot of great insights and guidance.&lt;/p&gt;
&lt;h2&gt;When things don&apos;t go according to plan&lt;/h2&gt;
&lt;h3&gt;git commit -am &quot;Bunch of stuff&quot;&lt;/h3&gt;
&lt;p&gt;It&apos;s a good practice to commit as often as possible, since it simplifies the work when writing the commit message, hence a cleaner Git history. But, sometimes things don&apos;t go as planned and we do too many changes without committing. Hopefully, it is possible to stage and commit code lines separately using &lt;code&gt;git add --path&lt;/code&gt;. You can read more about it in &lt;a href=&quot;http://codefoster.com/addpatch/&quot;&gt;The Git Add Patch Command&lt;/a&gt; article in which the author describes how to achieve it using the command line or in VSCode. I prefer to use a GUI to do that since I have a better overview at what I want to stage instead of going chunk by chunk using the CLI. It&apos;s actually the only reason I open a GUI for Git.&lt;/p&gt;
&lt;h3&gt;git commit -am &quot;Oops forgot a semicolon.&quot;&lt;/h3&gt;
&lt;p&gt;For some reason&amp;lt;sup&amp;gt;&lt;a href=&quot;#footnote1&quot;&gt;*&lt;/a&gt;&amp;lt;/sup&amp;gt;, an error passed through the linter and had been committed. We realise later when the CI is failing, and the PR rejected that we&apos;ve forgotten a semicolon. Bummer. Committing that alone won&apos;t be any value to the reviewer, and we might have to redo the fix if we need to revert to a previous state. Luckily the PR is still open so that we can add the missing semicolon in the right commit.&lt;/p&gt;
&lt;p&gt;If the error occurred in the last commit, we could write the fix, stage and commit using the &lt;code&gt;--amend&lt;/code&gt; flag. That way the change is part of the previous commit, incognito.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add -A
git commit --amend --no-edit
# Push the fix to the PR branch
git push origin --force-with-lease
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;You can &lt;a href=&quot;https://developer.atlassian.com/blog/2015/04/force-with-lease/&quot;&gt;read more about&lt;/a&gt; why you should use &lt;code&gt;--force-with-lease&lt;/code&gt; instead of &lt;code&gt;--force&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Otherwise, if the issue happened earlier in Git history, we need to go back in time using the powerful &lt;a href=&quot;https://www.atlassian.com/git/tutorials/rewriting-history/git-rebase&quot;&gt;&lt;code&gt;git rebase&lt;/code&gt;&lt;/a&gt;. First, we need to find where it happened and we have several options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Check the history of the given line where the issue is, using &lt;code&gt;git blame&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Check the log to read through the changes, using &lt;code&gt;git log --oneline&lt;/code&gt; or &lt;code&gt;git log -p&lt;/code&gt; (or using your favourite GUI).&lt;/li&gt;
&lt;li&gt;And if none of it worked, we can use &lt;a href=&quot;https://robots.thoughtbot.com/git-bisect&quot;&gt;&lt;code&gt;git bisect&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In my experience, since it&apos;s related to one of my commits, I can find it using the log.&lt;/p&gt;
&lt;p&gt;We have now found the commit, we can use its SHA hash and rebase on it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git rebase -i e39a40e39bc325cde31b71402a79afb7be7e31ef^
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;e38a...&lt;/code&gt; is the SHA hash&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;^&lt;/code&gt; at the end of the SHA hash is to specify that we want to start the rebase from this commit. Without it, it will begin at the next commit.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Git opens a new window&amp;lt;sup&amp;gt;&lt;a href=&quot;#footnote2&quot;&gt;**&lt;/a&gt;&amp;lt;/sup&amp;gt; where we specify the commands to run for each commit. Here, we replace &lt;code&gt;pick&lt;/code&gt; with &lt;code&gt;edit&lt;/code&gt; (or &lt;code&gt;e&lt;/code&gt; for short) on the first line:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;edit e39a40e The commit we want to update
pick 84da00a Another commit message

# Rebase e39a40e..84da00a onto e39a40e (2 commands)
#
# Commands:
# p, pick &amp;lt;commit&amp;gt; = use commit
# r, reword &amp;lt;commit&amp;gt; = use commit, but edit the commit message
# e, edit &amp;lt;commit&amp;gt; = use commit, but stop for amending
# s, squash &amp;lt;commit&amp;gt; = use commit, but meld into previous commit
# f, fixup &amp;lt;commit&amp;gt; = like &quot;squash&quot;, but discard this commit&apos;s log message
# x, exec &amp;lt;command&amp;gt; = run command (the rest of the line) using shell
# d, drop &amp;lt;commit&amp;gt; = remove commit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save the file, and Git brings us back at the commit.&lt;/p&gt;
&lt;p&gt;Now, all we have to do is to add the missing semicolon, stage the change, continue the rebase and finally push the change to the remote branch.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add -A
git rebase --continue
git push origin --force-with-lease
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;a name=&quot;footnote1&quot;&amp;gt;*&amp;lt;/a&amp;gt; I heard you like to commit using &lt;code&gt;-n&lt;/code&gt;? 👀&lt;/p&gt;
&lt;p&gt;&amp;lt;a name=&quot;footnote2&quot;&amp;gt;**&amp;lt;/a&amp;gt; it&apos;s possible to &lt;a href=&quot;https://stackoverflow.com/a/2596835&quot;&gt;configure Git&lt;/a&gt; to use your favourite editor.&lt;/p&gt;
</content:encoded></item><item><title>VSCode as a Vim editor</title><link>https://kewah.com/vscode-as-a-vim-editor/</link><guid isPermaLink="true">https://kewah.com/vscode-as-a-vim-editor/</guid><description>How to setup VSCode to feel at home for a Vim user.
</description><pubDate>Sat, 08 Sep 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In 2013 I started to use &lt;a href=&quot;/from-sublime-text-to-vim&quot;&gt;Vim as my primary editor&lt;/a&gt;, the journey hasn&apos;t been without frustration, but in general, I really enjoyed using it. It took me sometimes, but I found a setup that worked well for me from autocompletion to code navigation. What I love the most is &lt;a href=&quot;/from-sublime-text-to-vim/#magic-motions&quot;&gt;Vim motions&lt;/a&gt;, I would not trade that feature for something else.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So, if everything was fine why did you start using VSCode?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I got mainly interested by &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;VSCode&lt;/a&gt; for its debugging and refactoring features. I got used to debugging JavaScript or CSS in the browser&apos;s dev tools, but I never found the right spot for debugging Node apps. With VSCode it is possible to debug directly in the editor, to add breakpoints or investigate the stack trace.&lt;/p&gt;
&lt;p&gt;But, as usual, the first thing I do when I start to use a new code editor to install the Vim plugin.&lt;/p&gt;
&lt;h2&gt;VSCode Vim plugin&lt;/h2&gt;
&lt;p&gt;In a nutshell, &lt;a href=&quot;https://github.com/VSCodeVim/Vim&quot;&gt;VSCodeVim&lt;/a&gt; is really good. It is by far the best Vim integration I have tried outside Vim (or NeoVim) itself. It&apos;s fast, includes &lt;a href=&quot;https://github.com/VSCodeVim/Vim#-emulated-plugins&quot;&gt;plugins&lt;/a&gt; I love (&lt;a href=&quot;https://github.com/easymotion/vim-easymotion&quot;&gt;easymotion&lt;/a&gt;, &lt;a href=&quot;https://github.com/justinmk/vim-sneak&quot;&gt;sneak&lt;/a&gt;, &lt;a href=&quot;https://github.com/tpope/vim-surround&quot;&gt;surround&lt;/a&gt;), and supports all motions and most shortcuts I used to use.&lt;/p&gt;
&lt;p&gt;To get there I had to tweak the settings to be able to remap commands, but the great thing with this plugin is that we can remap loads of it!&lt;/p&gt;
&lt;p&gt;For instance, I use &lt;code&gt;jk&lt;/code&gt; to switch between insert and normal mode.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;vim.insertModeKeyBindings&quot;: [
    {
      &quot;before&quot;: [&quot;j&quot;, &quot;k&quot;],
      &quot;after&quot;: [&quot;&amp;lt;Esc&amp;gt;&quot;]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;You need to define that in User Settings. (Search for &lt;code&gt;Preferences: Open User Settings.&lt;/code&gt; in the command palette (&lt;code&gt;⇧⌘P&lt;/code&gt;).)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It is pretty straightforward, you define what mapping you want to use in &lt;code&gt;before&lt;/code&gt; and what output you want to get into &lt;code&gt;after&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I rely a lot on EasyMotion to move around (jump to a different line or a character) but I found the default setting quite annoying: I have a double press the leader key, instead of once in Vim, and then the action (for instance &lt;code&gt;j&lt;/code&gt; to go to a line below). But, it is possible to remap it!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;vim.normalModeKeyBindingsNonRecursive&quot;: [
    {
      &quot;before&quot;: [&quot;&amp;lt;leader&amp;gt;&quot;, &quot;j&quot;],
      &quot;after&quot;: [&quot;&amp;lt;leader&amp;gt;&quot;, &quot;&amp;lt;leader&amp;gt;&quot;, &quot;j&quot;]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can remap not only key bindings but also execute commands. For instance to toggle a &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=alefragnani.Bookmarks&quot;&gt;bookmark&lt;/a&gt; using &lt;code&gt;ma&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;vim.normalModeKeyBindingsNonRecursive&quot;: [
    {
      &quot;before&quot;: [&quot;m&quot;, &quot;a&quot;],
      &quot;after&quot;: [],
      &quot;commands&quot;: [
        {
          &quot;command&quot;: &quot;bookmarks.toggle&quot;,
          &quot;args&quot;: []
        }
      ]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I have been able to remap bindings to match my Vim memory muscles. I felt at home when starting to use VSCode.&lt;/p&gt;
&lt;p&gt;You can find my VSCodeVim settings on this &lt;a href=&quot;https://gist.github.com/kewah/a5d8c1eff08db1623f98593727d99353&quot;&gt;Gist&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Refactoring&lt;/h2&gt;
&lt;p&gt;VSCode has made some refactoring tasks easier than what I used to in Vim. One of my favourites is renaming a variable by pressing &lt;code&gt;f2&lt;/code&gt;. All occurence will be updated in the project. The same happens when you rename a file or move it to another folder, its path is updated where it is used.&lt;/p&gt;
&lt;p&gt;More recently I discovered that we could easily transform arrow function with implicit return:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const add = (a, b) =&amp;gt; a + b;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Put the cursor on &lt;code&gt;(&lt;/code&gt;, then press &lt;code&gt;ctrl+shift+r&lt;/code&gt; -&amp;gt;
&lt;code&gt;add braces to arrow function&lt;/code&gt;. It will convert it to:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const add = (a, b) =&amp;gt; {
  return a + b;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Super convenient when you need to debug a function.&lt;/p&gt;
&lt;p&gt;In general, I find it more comfortable to refactor in VSCode, the multiple cursors works better than on Vim, and I can still use visual block mode when I need it.&lt;/p&gt;
&lt;h2&gt;Debugging&lt;/h2&gt;
&lt;p&gt;So far, I have only used the &quot;attach to process&quot; debug mode. When you run node (or nodemon) with the &lt;code&gt;—inspect&lt;/code&gt; flag you can attach the process in VSCode to debug the application.&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;launch.json&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;version&quot;: &quot;0.2.0&quot;,
  &quot;configurations&quot;: [
    {
      &quot;type&quot;: &quot;node&quot;,
      &quot;request&quot;: &quot;attach&quot;,
      &quot;name&quot;: &quot;dev:server inspect&quot;,
      &quot;port&quot;: 5858,
      &quot;restart&quot;: true,
      &quot;protocol&quot;: &quot;inspector&quot;
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&apos;s an excellent way of debugging Node.js apps, so you don&apos;t need to open a Chrome dev tools to add breakpoints and put &lt;code&gt;debugger&lt;/code&gt; statement in your code.&lt;/p&gt;
&lt;h2&gt;Navigation&lt;/h2&gt;
&lt;p&gt;After some tuning, navigating in VSCode looks familiar to Vim:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Search a file using the fuzzy finder à la &lt;a href=&quot;https://github.com/ctrlpvim/ctrlp.vim&quot;&gt;CtrlP&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use the command palette to run commands when I don&apos;t think it is necessary to memorise a key binding. For instance, I use &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ziyasal.vscode-open-in-github&quot;&gt;Open In GitHub&lt;/a&gt; plugin to open the GitHub page in the browser to open a pull request. Instead of mapping it on a crazy multi-combo binding, I find it easier to use the command palette and search for the command.&lt;/li&gt;
&lt;li&gt;Jump between windows using &lt;code&gt;ctrl + hjkl&lt;/code&gt;. (More about it in &lt;a href=&quot;#what-i-miss-from-vim&quot;&gt;&quot;What I miss from Vim&quot;&lt;/a&gt; section.)&lt;/li&gt;
&lt;li&gt;Go to definition using &lt;code&gt;gf&lt;/code&gt;. (In Vim I used Ternjs to have this feature, &quot;60% of the time, it works every time&quot;.)&lt;/li&gt;
&lt;li&gt;Use the code outline to jump between symbols in a file using &lt;code&gt;cmd+shift+o&lt;/code&gt;. I&apos;m in love with this feature.&lt;/li&gt;
&lt;li&gt;Switch between tabs using &lt;code&gt;gt&lt;/code&gt; (next tab) or &lt;code&gt;gT&lt;/code&gt; (previous tab). &lt;code&gt;ctrl + tab&lt;/code&gt; comes handy when I have too many files open, which happen more often to me because of the poor window management support on VSCode.&lt;/li&gt;
&lt;li&gt;Jump to the next occurrence of the variable under the cursor using &lt;code&gt;*&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Jump to the &lt;a href=&quot;https://gist.github.com/kewah/a5d8c1eff08db1623f98593727d99353#file-vimusersettings-js-L205-L223&quot;&gt;next change&lt;/a&gt; using &lt;code&gt;&amp;lt;leader&amp;gt;n&lt;/code&gt; (next change) or &lt;code&gt;&amp;lt;leader&amp;gt;p&lt;/code&gt; (previous change).&lt;/li&gt;
&lt;li&gt;And of course, my beloved Vim motions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Language support&lt;/h2&gt;
&lt;p&gt;The VSCode community is active, so I haven&apos;t encountered a language that wasn&apos;t supported: JavaScript (+JSX), TypeScript, Sass, Elm, Elixir/Phoenix, or Haskell.&lt;/p&gt;
&lt;p&gt;Autocompletion is right most of the time, except for CSS or Sass files where it struggles to suggest class names defined in a template file. It also has some issues to autocomplete snippets. If I type too fast, I will need to close and reopen the suggestion box to be able to run a snippet.&lt;/p&gt;
&lt;h2&gt;What I miss from Vim&lt;/h2&gt;
&lt;p&gt;Obviously, everything is not perfect. The main thing I miss from Vim is the window management, in VSCode the current support is really basic.&lt;/p&gt;
&lt;p&gt;In Vim, it is possible to open several files under the same tab, so it is straightforward to create different spaces with related files and terminal open. For instance, you can open a tab containing all files related to a component.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;| TAB A |
----------------------------------------------------------------------
Module.js     | Module.test.js     | Terminal that runs
              |                    | Module.test.js
              |--------------------|
              | Module.style.scss  |
              |                    |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In VSCode, tabs are all independent, so even if I can split the screen in two, I can&apos;t switch tabs to change both files at the same time. So, I need to jump back and forth between the files to switch the tabs accordingly. Annoying.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;| TAB Module.js | TAB Module.test.js | TAB Terminal
---------------------------------------------------------------------------
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It creates some frustration when I try to jump quickly between files and terminals since I need to remember where are the related files are and keep things in sync manually.&lt;/p&gt;
&lt;h2&gt;What&apos;s next?&lt;/h2&gt;
&lt;p&gt;I will keep using VSCode and haven&apos;t planned to go back to my previous setup iTerm + NeoVim. It has (almost) everything I&apos;m looking for: it is fast, it has good language, and autocompletion support, a built-in terminal, and I can still use Vim in it. A nice thing is that most of my colleagues use it as well, so we can share settings in a project or do remote pair programming in VSCode directly, which is fantastic.&lt;/p&gt;
</content:encoded></item><item><title>How to create declaration files for JavaScript dependencies in TypeScript</title><link>https://kewah.com/typescript-declaration-file/</link><guid isPermaLink="true">https://kewah.com/typescript-declaration-file/</guid><description>Walk through on how to write a local declaration file for a JavaScript dependency installed using npm.
</description><pubDate>Wed, 15 Aug 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;In this article, I used TypeScript v3.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In TypeScript, declaration files (&lt;code&gt;.d.ts&lt;/code&gt;) are used to describe the shape of a JavaScript module. By default, TypeScript can&apos;t infer types, so you need to define these files to help the type checker, but also to get better autocompletion in your code editor. When importing a dependency into a TypeScript code base, you might get this error.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;error TS7016: Could not find a declaration file for module &apos;my-dependency&apos;.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first solution that could be used in case you are starting to use TypeScript in an existing JavaScript project is to set &lt;code&gt;noImplicitAny&lt;/code&gt; in your &lt;code&gt;tsconfig.json&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;compilerOptions&quot;: {
    &quot;noImplicitAny&quot;: false
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TypeScript will assume that non annotated properties have the type &lt;code&gt;any&lt;/code&gt;. This is helpful to update the codebase gradually, but you lose TypeScript safety.&lt;/p&gt;
&lt;p&gt;The second option is to check if your dependency has a declaration file already created in the &lt;a href=&quot;https://github.com/DefinitelyTyped/DefinitelyTyped/&quot;&gt;DefinitelyTyped&lt;/a&gt; repository. You can search it on &lt;a href=&quot;https://microsoft.github.io/TypeSearch/&quot;&gt;TypeSearch&lt;/a&gt; website. If you are lucky, you will find it, and you need to install it as a dependency.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install --save-dev @types/my-dependency
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But sometimes you won&apos;t find it, and that leads us to the third solution, writing your own declaration file.&lt;/p&gt;
&lt;h2&gt;Writing you own declaration file&lt;/h2&gt;
&lt;p&gt;You can find different &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/declaration-files/templates.html&quot;&gt;declaration file templates&lt;/a&gt; in the official documentation, but let&apos;s go through an example.&lt;/p&gt;
&lt;p&gt;You have imported &lt;code&gt;koa-static-server&lt;/code&gt; module, and it doesn&apos;t have a declaration file in DefinitelyTyped. So, as you&apos;ve seen earlier in the article, you need to write one locally.&lt;/p&gt;
&lt;p&gt;First, you need to update &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/tsconfig-json.html#types-typeroots-and-types&quot;&gt;&lt;code&gt;typeRoots&lt;/code&gt;&lt;/a&gt; in &lt;code&gt;tsconfig.json&lt;/code&gt; to let the compiler know where to find the local declaration files.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;compilerOptions&quot;: {
    &quot;typeRoots&quot;: [&quot;./node_modules/@types/&quot;, &quot;./src/@types&quot;]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, you can write the local declaration file in &lt;code&gt;src/@types&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And finally, you can create the declaration file for &lt;code&gt;koa-static-server&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir src/@types/koa-static-server/index.d.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;In this example I haven&apos;t put the entire type declaration for this module.&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;declare module &apos;koa-static-server&apos; {
  import Koa from &apos;koa&apos;;

  interface Options {
    rootDir: string;
    rootPath: string;
    gzip: boolean;
  }

  export default function staticServer(options: Options): Koa.Middleware;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;declare&lt;/code&gt; statement declares the module on a global level, so we don&apos;t need to import it in our code. TypeScript does it automatically via the &lt;code&gt;typeRoots&lt;/code&gt; flag. The code inside the module declaration is similar to a TypeScript module: you expose the functions with their types that are defined in the JavaScript module.&lt;/p&gt;
&lt;p&gt;I recommend you to check the &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/declaration-files/templates.html&quot;&gt;official documentation&lt;/a&gt; to have more examples, but keep in mind that some of them use an older TypeScript&apos;s syntax such as &lt;code&gt;export = staticServer&lt;/code&gt; instead of &lt;code&gt;export default&lt;/code&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Refactoring a Redux application using redux-actions</title><link>https://kewah.com/refactoring-redux-application-using-redux-actions/</link><guid isPermaLink="true">https://kewah.com/refactoring-redux-application-using-redux-actions/</guid><description>Walk through on how to refactor a Redux application using redux-actions
</description><pubDate>Wed, 04 Jul 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Redux is widely used and a great way to manage your data flow in a React app, but a common complaint is that Redux requires a lot of boilerplate code. I was looking for a way to reduce the boilerplate while keeping code expressiveness. In the official Redux documentation, they mention different solutions, but my attention got drawn by &lt;a href=&quot;https://github.com/redux-utilities/redux-actions&quot;&gt;redux-actions&lt;/a&gt;. It follows &lt;a href=&quot;https://github.com/redux-utilities/flux-standard-action&quot;&gt;Flux Standard Action&lt;/a&gt; convention and has a small API that covers the creation and handling of actions.&lt;/p&gt;
&lt;p&gt;In this example, we have a &lt;a href=&quot;https://github.com/erikras/ducks-modular-redux&quot;&gt;duck module&lt;/a&gt; that manages to fetch and store articles. We use &lt;a href=&quot;https://github.com/reduxjs/redux-thunk&quot;&gt;redux-thunk&lt;/a&gt; for handling asynchronous actions.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import * as api from &apos;../api/articles&apos;;

// Types
export const BEGIN_REQUEST = &apos;ARTICLES/BEGIN_REQUEST&apos;;
export const GET_ARTICLES = &apos;ARTICLES/GET_ARTICLES&apos;;
export const GET_ARTICLES_COMPLETE = &apos;ARTICLES/GET_ARTICLES_COMPLETE&apos;;

export const status = {
  LOADING: &apos;LOADING&apos;,
  ERROR: &apos;ERROR&apos;,
  COMPLETE: &apos;COMPLETE&apos;,
};

// Actions
export const beginRequest = () =&amp;gt; ({
  type: BEGIN_REQUEST,
});

export const getArticlesComplete = (result) =&amp;gt; {
  if (result instanceof Error) {
    return {
      type: GET_ARTICLES_COMPLETE,
      payload: result,
      error: true,
    };
  }

  return {
    type: GET_ARTICLES_COMPLETE,
    payload: result,
  };
};

export const getArticles = () =&amp;gt; (dispatch) =&amp;gt; {
  dispatch(beginRequest());

  return api.getArticles().then(
    (entities) =&amp;gt; {
      dispatch(getArticlesComplete(entities));
    },
    (error) =&amp;gt; {
      dispatch(getArticlesComplete(error));
    }
  );
};

// Reducer
const initialState = {
  status: null,
  error: null,
  entities: null,
};

export default (state = initialState, { type, payload, error }) =&amp;gt; {
  switch (type) {
    case BEGIN_REQUEST:
      return {
        ...state,
        status: status.LOADING,
      };

    case GET_ARTICLES_COMPLETE:
      if (error) {
        return {
          ...state,
          status: status.COMPLETE,
          error: payload,
        };
      }

      return {
        ...state,
        status: status.COMPLETE,
        entities: [...state.entities, ...payload],
        error: null,
      };

    default:
      return state;
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This example is a &quot;classic&quot; way of defining action creators and a reducer. Where I think we can reduce boilerplate is when we need to handle different payloads’ states, depending on if it is an error or the entities. Every time we need to create a &quot;complete&quot; action creator, like &lt;code&gt;getArticlesComplete&lt;/code&gt;, we have to check the type of the payload to know if it is a failure or a success. This code is redundant and doesn&apos;t bring valuable information, and thereby it can be extracted into a helper function.&lt;/p&gt;
&lt;h2&gt;Refactoring using redux-actions&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Actions&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import { createAction } from &apos;redux-actions&apos;;

// ...

export const beginRequest = createAction(BEGIN_REQUEST);
export const getArticlesComplete = createAction(GET_ARTICLES_COMPLETE);

// ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;redux-actions has a helper &lt;code&gt;createAction&lt;/code&gt; that does the work for us to handle the different payloads’ states. Moreover, by using this helper, we know that these actions can have success and failure states.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: We haven&apos;t yet refactored &lt;code&gt;getArticles&lt;/code&gt; since it is an async action and has a different logic.&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reducer&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import { handleActions } from &apos;redux-actions&apos;;

// ...

export default handleActions(
  {
    [BEGIN_REQUEST]: (state) =&amp;gt; ({
      ...state,
      status: status.LOADING,
    }),

    [GET_ARTICLES_COMPLETE]: {
      next: (state, { payload }) =&amp;gt; ({
        ...state,
        status: status.COMPLETE,
        entities: [...state.entities, ...payload],
        error: null,
      }),

      throw: (state, { payload }) =&amp;gt; ({
        ...state,
        status: status.COMPLETE,
        error: payload,
      }),
    },
  },
  initialState
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;handleActions&lt;/code&gt; let us define our reducer. The big change is that we handle the success and failure states using &lt;code&gt;next&lt;/code&gt; (success) and &lt;code&gt;throw&lt;/code&gt; (failure) instead of using the &lt;code&gt;if&lt;/code&gt; statement. The positive part is that now each reducer is defined into separated functions, so it prevents data leaking between action types. Other than that, the &lt;code&gt;handleActions&lt;/code&gt; solution is close to the switch statement in terms of readability.&lt;/p&gt;
&lt;h2&gt;Moving forward using reduce-reducers&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import { createAction, handleAction } from &apos;redux-actions&apos;;
import reduceReducers from &apos;reduce-reducers&apos;;

// ...

const initialState = {
  status: null,
  error: null,
  entities: null,
};

// BEGIN_REQUEST

export const beginRequest = createAction(BEGIN_REQUEST);

const beginRequestReducer = handleAction(
  BEGIN_REQUEST,
  (state) =&amp;gt; ({
    ...state,
    status: status.LOADING,
  }),
  initialState
);

// GET_ARTICLES

export const getArticlesComplete = createAction(GET_ARTICLES_COMPLETE);

export const getArticles = () =&amp;gt; (dispatch) =&amp;gt; {
  dispatch(beginRequest());

  return api.getArticles().then(
    (entities) =&amp;gt; {
      dispatch(getArticlesComplete(entities));
    },
    (error) =&amp;gt; {
      dispatch(getArticlesComplete(error));
    }
  );
};

const getArticlesReducer = handleAction(
  GET_ARTICLES_COMPLETE,
  {
    next: (state, { payload }) =&amp;gt; ({
      ...state,
      status: status.COMPLETE,
      entities: [...state.entities, ...payload],
      error: null,
    }),

    throw: (state, { payload }) =&amp;gt; ({
      ...state,
      status: status.COMPLETE,
      error: payload,
    }),
  },
  initialState
);

export default reduceReducers(beginRequestReducer, getArticlesReducer);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/redux-utilities/reduce-reducers&quot;&gt;reduce-reducers&lt;/a&gt; allows us to reduce multiple reducers into a single reducer. So, what it means is that we can define each reducer (&lt;code&gt;BEGIN_REQUEST&lt;/code&gt;, and &lt;code&gt;GET_ARTICLES_COMPLETE&lt;/code&gt;) into their own &lt;code&gt;handleAction&lt;/code&gt;. What I like about this solution is that we can gather related code from the action type to the action creator and the reducer and put it in the same part of the ducks file.&lt;/p&gt;
</content:encoded></item><item><title>ESLint and Prettier for a better team collaboration</title><link>https://kewah.com/eslint-and-prettier/</link><guid isPermaLink="true">https://kewah.com/eslint-and-prettier/</guid><description>How we use ESLint and Prettier to share the same coding style across repositories
</description><pubDate>Tue, 29 May 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When we work in a team of developers we all have our way of programming which may end up with an archaeology type of codebase, where we can guess who has written which part just by the way the code is formatted. You get distracted when you read the code due to a stylistic difference, and you lose time in PRs for nitpicking feedback. The code is also harder to maintain since you may end up with Git diffs related only to formatting. For these reasons, it&apos;s important to share the same coding style.&lt;/p&gt;
&lt;p&gt;Luckily the process of making sure that all code follows the same style is easily automated. Linting and formatting before committing the code ensure that it looks the same, no matter who writes it.&lt;/p&gt;
&lt;p&gt;At Acast, we have decided to use &lt;a href=&quot;https://eslint.org/&quot;&gt;ESLint&lt;/a&gt; coupled with &lt;a href=&quot;https://prettier.io/&quot;&gt;Prettier&lt;/a&gt;. ESLint is a JavaScript linting and style checking tool that warns you when your code is not following given rules, but it doesn&apos;t reformat your code. That&apos;s where Prettier comes into play. Prettier is a fantastic tool that entirely takes the responsibility to format your code. It&apos;s opinionated which means it has only a few settings you can customize. Moreover, it is well integrated with ESLint, so it&apos;s possible to run Prettier via &lt;code&gt;eslint --fix&lt;/code&gt; command, which reduces the number of commands that you have to run to lint and format the codebase. Additionally, we chose to use the &lt;a href=&quot;https://github.com/airbnb/javascript#readme&quot;&gt;ESLint config from Airbnb&lt;/a&gt; because it&apos;s popular amongst the JavaScript community and to avoid lengthy discussions around what rules we should define in our config. We want to override as few rules as possible to avoid entering lengthy debates loop for little reward.&lt;/p&gt;
&lt;h2&gt;How to use it.&lt;/h2&gt;
&lt;p&gt;To include it into your project, you can check the README file in the &lt;a href=&quot;https://github.com/acastSthlm/eslint-config-acast&quot;&gt;eslint-config-acast&lt;/a&gt; project or do these two steps:&lt;/p&gt;
&lt;p&gt;Install the needed packages:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install --save-dev eslint-config-acast eslint prettier
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create a .eslintrc file and add the following to it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;extends&quot;: &quot;acast&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you are good to go!&lt;/p&gt;
&lt;h2&gt;Automate all the things!&lt;/h2&gt;
&lt;p&gt;To be able to lint your changes before committing I recommend to use &lt;a href=&quot;https://github.com/okonet/lint-staged/&quot;&gt;lint-staged&lt;/a&gt; and &lt;a href=&quot;https://github.com/typicode/husky&quot;&gt;husky&lt;/a&gt;. They allow you to run npm tasks on Git hooks events, in our case in &lt;code&gt;precommit&lt;/code&gt; hook.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install --save-dev husky lint-staged
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In your &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;husky&quot;: {
    &quot;hooks&quot;: {
      &quot;pre-commit&quot;: &quot;lint-staged&quot;
    }
  },
  &quot;lint-staged&quot;: {
    &quot;*.js&quot;: [&quot;eslint --fix&quot;, &quot;git add&quot;, &quot;eslint --max-warnings=0&quot;]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, every time you commit a change, &lt;code&gt;lint-staged&lt;/code&gt; runs &lt;code&gt;eslint&lt;/code&gt; against files that have been staged to fix formatting changes. Then, it stages them back and runs &lt;code&gt;eslint&lt;/code&gt; again to detect other errors. The commit is aborted if there is an error.&lt;/p&gt;
</content:encoded></item><item><title>How to test promises with Mocha</title><link>https://kewah.com/how-to-test-promises-with-mocha/</link><guid isPermaLink="true">https://kewah.com/how-to-test-promises-with-mocha/</guid><description>How to test promises with Mocha
</description><pubDate>Mon, 18 Apr 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Promises are convenient to deal with asynchronous code in JavaScript, but at first glance it can feels tedious when it comes to unit test them.&lt;/p&gt;
&lt;p&gt;The first approach might be to unit test promises in the same way as we do with callbacks: by calling the done() function, passed by Mocha, when the promise is completed.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;it(&apos;should do something async&apos;, function (done) {
  asyncCall().then(function (result) {
    expect(result).to.be(&apos;foo&apos;);
    done();
  });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It works fine if the test passes but if it fails, then we don&apos;t get any error report from mocha, at best a timeout error. This is because we don&apos;t handle the case when the promise is rejected. This is where it gets tedious and adds more boilerplate: every time we test a promise, we end up having several done() calls in our test. (which bothers me.)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;it(&apos;should do something async&apos;, function (done) {
  asyncCall()
    .then(function (result) {
      expect(result).to.be(&apos;foo&apos;);
      done();
    })
    .catch(done);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Built in Promises support&lt;/h2&gt;
&lt;p&gt;Hopefully, Mocha comes with a nice built in support. Instead of having to handle manually failing tests, we can directly return the promise.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;it(&apos;should do something async&apos;, function () {
  return asyncCall().then(function (result) {
    expect(result).to.be(&apos;foo&apos;);
  });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And voilà! Now we don&apos;t need to think about where we need to put the &lt;code&gt;done()&lt;/code&gt; calls.&lt;/p&gt;
&lt;p&gt;If you want to directly assert your promises, I recommend you to look over &lt;a href=&quot;http://chaijs.com/plugins/chai-as-promised/&quot;&gt;Chai promises plugin&lt;/a&gt;.&lt;/p&gt;
</content:encoded></item><item><title>Tools to keep a consistent coding style in JavaScript</title><link>https://kewah.com/tools-to-keep-a-consistent-coding-style-in-javascript/</link><guid isPermaLink="true">https://kewah.com/tools-to-keep-a-consistent-coding-style-in-javascript/</guid><description>Using ESLint and Prettier to keep a consistent coding style in JavaScript
</description><pubDate>Mon, 09 Mar 2015 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&quot;Code formatting is important. It is too important to ignore and it is important to treat religiously. Code formatting is about communication, and communication is the professional developer&apos;s first order of business.&quot;&lt;/p&gt;
&lt;p&gt;-- Robert C. Martin.
&lt;a href=&quot;http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882&quot;&gt;Clean code: A handbook of Agile software craftmanship&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Update: I now use ESLint and Prettier to keep a consistent coding style. You can find more information &lt;a href=&quot;/eslint-and-prettier/&quot;&gt;in this article&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://editorconfig.org/&quot;&gt;EditorConfig&lt;/a&gt; is the first tool I used to make sure people in the team used the same basic settings, such as trailing whitespace &amp;amp; indent style/size, mainly to simplify git merging. Nevertheless, I was looking for tools more focused on JavaScript, in addition to EditorConfig, in order to share the same coding style.&lt;/p&gt;
&lt;p&gt;In JavaScript there are plenty of tools to help you to keep your code clean. Like &lt;a href=&quot;https://github.com/jshint/jshint/&quot;&gt;JSHint&lt;/a&gt;, &lt;a href=&quot;https://github.com/jscs-dev/node-jscs&quot;&gt;JSCS&lt;/a&gt;, &lt;a href=&quot;https://github.com/jshint/fixmyjs&quot;&gt;fixmyjs&lt;/a&gt;, &lt;a href=&quot;https://github.com/beautify-web/js-beautify&quot;&gt;JS Beautifier&lt;/a&gt;, and other that I haven&apos;t tested yet.&lt;/p&gt;
&lt;p&gt;I chose to use &lt;a href=&quot;http://eslint.org/&quot;&gt;ESLint&lt;/a&gt; to lint my JavaScript code and &lt;a href=&quot;https://github.com/millermedeiros/esformatter&quot;&gt;esformatter&lt;/a&gt; to format it. The main reason I chose these ones is the customisability: they are pluggable and they have a large range of settings to really fit my coding style.&lt;/p&gt;
&lt;h2&gt;ESLint&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://eslint.org/&quot;&gt;ESLint&lt;/a&gt; detects errors or problems in your code, as well as JSHint. For instance it raises an error if a variable in not declared. Moreover it also checks the coding style based on &lt;a href=&quot;http://eslint.org/docs/rules/#stylistic-issues&quot;&gt;rules&lt;/a&gt;. It will warm you in case you missed a space after &lt;code&gt;if&lt;/code&gt; for example.&lt;/p&gt;
&lt;p&gt;You specify the &lt;a href=&quot;http://eslint.org/docs/rules/&quot;&gt;rules&lt;/a&gt; in a configuration file &lt;code&gt;.eslinrc&lt;/code&gt;. It could be globally, saved in your $HOME, or locally at the root of your project. Here is my &lt;a href=&quot;https://github.com/kewah/dotfiles/blob/master/dotfiles/eslintrc&quot;&gt;.eslintrc&lt;/a&gt; as an example, and you can find more information about the &lt;a href=&quot;http://eslint.org/docs/configuring/&quot;&gt;configuration&lt;/a&gt; in the project documentation.&lt;/p&gt;
&lt;p&gt;You can use it in different way, either with the &lt;a href=&quot;http://eslint.org/docs/command-line-interface/&quot;&gt;command line&lt;/a&gt;, or your favourite tasks runner or directly in your text editor. (See the &lt;a href=&quot;http://eslint.org/docs/integrations/&quot;&gt;official plugins list&lt;/a&gt;.) I prefer to use it in my code editor (in Vim through &lt;a href=&quot;https://github.com/scrooloose/syntastic&quot;&gt;syntastic&lt;/a&gt;), thereby I get direct feedback and it helps me to avoid errors while coding.&lt;/p&gt;
&lt;p&gt;So far I really like it, it&apos;s in between JSHint and JSCS (which is only focused on the coding style). The list of rules cover almost everything. So if you like JSHint, I definitively recommend you to give a try to ESLint.&lt;/p&gt;
&lt;h2&gt;esformatter&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/millermedeiros/esformatter&quot;&gt;esformatter&lt;/a&gt; beautifies your code based on a &lt;a href=&quot;https://github.com/millermedeiros/esformatter/blob/master/lib/preset/default.json&quot;&gt;set of rules&lt;/a&gt;. As ESLint, you define the rules in a .esformatter file, which could be global ($HOME) or local (project&apos;s root). I recommend you to try out &lt;a href=&quot;http://lloiser.github.io/esformatter-visualize/&quot;&gt;esformatter-visualize&lt;/a&gt; that will help you out to configure your settings.&lt;/p&gt;
&lt;p&gt;You can use esformatter through &lt;a href=&quot;https://github.com/millermedeiros/esformatter#cli&quot;&gt;command line interface&lt;/a&gt;, tasks runner (&lt;a href=&quot;https://github.com/sindresorhus/gulp-esformatter&quot;&gt;Gulp&lt;/a&gt;, &lt;a href=&quot;https://github.com/jzaefferer/grunt-esformatter&quot;&gt;Grunt&lt;/a&gt; and Brunch) and text editor (&lt;a href=&quot;https://github.com/millermedeiros/vim-esformatter&quot;&gt;Vim&lt;/a&gt;, &lt;a href=&quot;https://github.com/piuccio/sublime-esformatter&quot;&gt;Sublime Text&lt;/a&gt; and &lt;a href=&quot;https://github.com/sindresorhus/atom-esformatter&quot;&gt;Atom&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Like ESLint, I prefer to use esformatter inside Vim. It lets me code quickly without worrying too much about code formatting and I trigger the command (&lt;code&gt;&amp;lt;leader&amp;gt;es&lt;/code&gt;) to beautify it.&lt;/p&gt;
&lt;p&gt;It is still &quot;work in progress&quot; but it does a pretty great job so far. It starts to have more and more &lt;a href=&quot;https://github.com/millermedeiros/esformatter/wiki/Plugins&quot;&gt;plugins&lt;/a&gt; to fit specific needs, like using &lt;a href=&quot;https://github.com/brix/esformatter-onevar&quot;&gt;one var&lt;/a&gt; or &lt;a href=&quot;https://github.com/twolfson/esformatter-var-each&quot;&gt;multiple var&lt;/a&gt; statement. I look forward to be able to use one configuration file for both ESLint and esformatter as they are almost using the same rules.&lt;/p&gt;
&lt;p&gt;If you are interested, here is my &lt;a href=&quot;https://github.com/kewah/dotfiles/blob/master/dotfiles/esformatter&quot;&gt;.esformatter&lt;/a&gt; file.&lt;/p&gt;
&lt;h2&gt;ES6 &amp;amp; JSX&lt;/h2&gt;
&lt;p&gt;If you are using ES6 and/or JSX, ESLint already supports JSX and partially ES6. The team of esformatter is working to &lt;a href=&quot;https://github.com/millermedeiros/esformatter/issues/192&quot;&gt;support ES6 features&lt;/a&gt; and there are plugins that fill the gap for JSX. I use &lt;a href=&quot;https://github.com/royriojas/esformatter-jsx-ignore&quot;&gt;esformatter-jsx-ignore&lt;/a&gt; that &quot;ignore JSX blocks so the rest of the javascript code could be formatted&quot;.&lt;/p&gt;
&lt;p&gt;Feel free to share your experience or tools you like to use to maintain your coding style.&lt;/p&gt;
</content:encoded></item><item><title>npm as a front-end package manager</title><link>https://kewah.com/npm-as-a-front-end-package-manager/</link><guid isPermaLink="true">https://kewah.com/npm-as-a-front-end-package-manager/</guid><description>How to use npm as a front-end package manager
</description><pubDate>Mon, 19 May 2014 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Vous pouvez lire la version française de &lt;a href=&quot;http://putaindecode.fr/posts/frontend/npm-comme-package-manager-pour-le-front-end/&quot;&gt;cet article&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;npm has played an important role in Node.js success. It facilitates the creation, sharing and installation of packages and allows developers to follow the &lt;a href=&quot;http://www.faqs.org/docs/artu/ch01s06.html&quot;&gt;Unix philosophy&lt;/a&gt; where each module &quot;do one thing well&quot;. (Avoids complexity, facilitates code reusability and testing.)&lt;/p&gt;
&lt;p&gt;However, npm isn&apos;t limited to JavaScript modules. It&apos;s possible to publish any type of file and this is where it gets interesting for our front-end modules, which can be CSS, HTML, fonts, etc..&lt;/p&gt;
&lt;h2&gt;JavaScript module&lt;/h2&gt;
&lt;p&gt;If you are not used to creating packages, I recommend you run the command &lt;code&gt;npm init&lt;/code&gt; which helps you to generate the &lt;code&gt;package.json&lt;/code&gt; (this file contains all the information used by &lt;code&gt;npm&lt;/code&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;my-javascript-module&quot;,
  &quot;version&quot;: &quot;0.0.1&quot;,
  &quot;main&quot;: &quot;index.js&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want to write a module that work everywhere: AMD (RequireJS) , CommonJS (Browserify) or directly with the &lt;code&gt;window&lt;/code&gt; object, you should take a look to &lt;a href=&quot;https://github.com/umdjs/umd&quot;&gt;UMD patterns&lt;/a&gt; repository. There are different types of wrapper to export your API. For instance:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(function (root, name, definition) {
  if (typeof define === &apos;function&apos; &amp;amp;&amp;amp; define.amd) {
    define([], definition);
  } else if (typeof module === &apos;object&apos; &amp;amp;&amp;amp; module.exports) {
    module.exports = definition();
  } else {
    root[name] = definition();
  }
})(this, &apos;ModuleName&apos;, function () {
  // Here, you define the API of your module.

  // You can return a value, a function or like
  // in this example, an object.
  return {};
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Module for other file types&lt;/h2&gt;
&lt;p&gt;In the &lt;code&gt;[files](https://docs.npmjs.com/files/package.json#files)&lt;/code&gt; field, we can define the list of files we want to publish (CSS, fonts, SVG, etc.). For instance, a &lt;code&gt;package.json&lt;/code&gt; for a CSS grid module.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;grid-system&quot;,
  &quot;version&quot;: &quot;0.0.1&quot;,
  &quot;files&quot;: [
    &quot;grid.css&quot;
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you have &lt;a href=&quot;https://gist.github.com/coolaj86/1318304&quot;&gt;published&lt;/a&gt; and installed (&lt;code&gt;npm install grid-system --save&lt;/code&gt;) the package, the &lt;code&gt;grid.css&lt;/code&gt; file will be located in &lt;code&gt;node_modules/grid-system/grid.css&lt;/code&gt;. Therefore you can include it in your HTML files or &lt;code&gt;@import&lt;/code&gt; it inside your Sass files. (The Sass option &lt;code&gt;--load-path&lt;/code&gt; is really useful if you don&apos;t want to have to specify the path to node_modules each time you import a module.)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;link href=&quot;node_modules/grid-system/grid.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Also, if you need to publish different versions of your JavaScript module (dev, prod, minified) you can add them to the &quot;files&quot; field as well.&lt;/p&gt;
&lt;h2&gt;How to install a module without a package.json&lt;/h2&gt;
&lt;p&gt;Sometimes you find the perfect module that fits your needs, unfortunately it doesn&apos;t have a &lt;code&gt;package.json&lt;/code&gt;. Therefore you can&apos;t install it with npm. You have two options that can solve this:&lt;/p&gt;
&lt;h3&gt;Solution 1: Fork the repository&lt;/h3&gt;
&lt;p&gt;The first solution is to fork the repository, add the missing &lt;code&gt;package.json&lt;/code&gt; and submit a pull request. In the meantime you can &lt;a href=&quot;https://www.npmjs.org/doc/cli/npm-install.html&quot;&gt;install&lt;/a&gt; the package using the Github namespace: &lt;code&gt;npm install yourUsername/the-perfect-module --save&lt;/code&gt;. But sometimes people don&apos;t want to &quot;pollute&quot; their repository with a bunch of configuration files and reject your PR.&lt;/p&gt;
&lt;h3&gt;Solution 2: Napa&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/shama/napa&quot;&gt;Napa&lt;/a&gt; is &quot;a helper for installing repos without a &lt;code&gt;package.json&lt;/code&gt; with npm&quot;. Once you have installed it, you are able to add the perfect module as a dependency of your project. Napa will download the entire repository in &lt;code&gt;node_modules&lt;/code&gt; directory. Napa&apos;s &lt;a href=&quot;https://github.com/shama/napa/blob/master/README.md&quot;&gt;readme&lt;/a&gt; file is well detailed, I don&apos;t think it&apos;s necessary to c/c everything here.&lt;/p&gt;
&lt;p&gt;For instance, a project using Three.js:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;my-project&quot;,
  &quot;version&quot;: &quot;0.0.1&quot;,
  &quot;scripts&quot;: {
    &quot;install&quot;: &quot;napa&quot;
  },
  &quot;napa&quot;: {
    &quot;threejs&quot;: &quot;mrdoob/three.js#r67&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;napa&quot;: &quot;^0.4.1&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why npm rather than another package manager:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It has already proved its worth with Node.js.&lt;/li&gt;
&lt;li&gt;No need to install new tools, you just need a package.json.&lt;/li&gt;
&lt;li&gt;You can manage all your dependencies with npm: tasks runner (Gulp, Grunt, etc.), JavaScript modules, tests, CSS, assets... everything!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I think it&apos;s important to keep things simple, starting with using only one package manager. Especially when you just need to download packages. (Some, like &lt;a href=&quot;https://github.com/component/component&quot;&gt;Component&lt;/a&gt;, have a build system in addition to the dependency manager.)&lt;/p&gt;
&lt;p&gt;So, why do you need Bower?&lt;/p&gt;
</content:encoded></item><item><title>Build a Web app with component(1)</title><link>https://kewah.com/build-a-web-app-with-component/</link><guid isPermaLink="true">https://kewah.com/build-a-web-app-with-component/</guid><description>How to build a web app using component(1)
</description><pubDate>Mon, 17 Feb 2014 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://github.com/component/component&quot;&gt;Component(1)&lt;/a&gt; is a front-end package manager created by &lt;a href=&quot;https://github.com/visionmedia&quot;&gt;TJ Holowaychuk&lt;/a&gt;. It embraces the philosophy of creating small and reusable modules. It is not restricted to JavaScript, indeed we are able to create components that also contain CSS, HTML, JSON, images, fonts, ... Therefore we can create JavaScript libraries, UI component or reusable &lt;a href=&quot;https://github.com/suitcss&quot;&gt;CSS utility classes&lt;/a&gt;. It allows us to organize applications around multiple components.&lt;/p&gt;
&lt;h2&gt;All in one place&lt;/h2&gt;
&lt;p&gt;Currently, the common architecture is to split files into different directories. For instance, this is how the files tree may look like for a menu:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;├── assets
│   └── images
│       └── menu
│           └── background.jpg
├── scripts
│   └── views
│       └── menu.js
├── styles
│   └── modules
│       └── menu.css
└── templates
    └── menu.html
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With component(1), we can regroup the logic and UI that define the menu component.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;├── app
│   └── menu
│       ├── background.jpg
│       ├── demo.html
│       ├── index.js
│       ├── menu.css
│       ├── template.html
│       └── test
│           └── menu.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We find all information about this component in one place. We should also add tests to be sure the component is bug free, before integrating it to the rest of the application. A readme file or demo page could help our colleagues, other developers or the future &quot;me&quot; to understand easily what the component does and how it works. Nothing really new, but in my opinion it simplifies the process a lot.&lt;/p&gt;
&lt;h2&gt;Registry system&lt;/h2&gt;
&lt;p&gt;There are a few things to know about component(1) registry system. It is based on GitHub style namespacing username/repository. It avoids name conflict, therefore you can choose the one you desire. To share a component with the community, you need to push it on GitHub, then add the repository link into the &lt;a href=&quot;https://github.com/component/component/wiki/Components&quot;&gt;wiki page&lt;/a&gt; to register it. The component will be accessible on &lt;a href=&quot;http://component.io/&quot;&gt;component.io&lt;/a&gt; or via the command &lt;code&gt;component search&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However you don&apos;t need to register a component to be able to install it.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;To install component(1), Node.js is required.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ npm install -g component
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;component(1) cli commands&lt;/h3&gt;
&lt;p&gt;Component(1) comes with multiple commands. You can printout the list with &lt;code&gt;component --help&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here is those I use often:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;search [query]&lt;/code&gt;: search registered component.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wiki&lt;/code&gt;: open the components list page. Another way to find components.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;create [dir]&lt;/code&gt;: create a component skeleton.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;install [name ...]&lt;/code&gt;: install one or more components.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;build&lt;/code&gt;: build the component. I will talk about it later.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Create a component&lt;/h3&gt;
&lt;p&gt;In this example we will create a progress bar component. First of all, we have to create a &lt;code&gt;component.json&lt;/code&gt; file in which we add all the information about the component.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;progress-bar&quot;,
  &quot;repo&quot;: &quot;kewah/progress-bar&quot;,
  &quot;description&quot;: &quot;Visual indicator of progress&quot;,
  &quot;version&quot;: &quot;0.0.1&quot;,
  &quot;keywords&quot;: [&quot;progress&quot;, &quot;bar&quot;],
  &quot;scripts&quot;: [
    &quot;index.js&quot;
  ],
  &quot;styles&quot;: [
    &quot;progress-bar.css&quot;
  ],
  &quot;templates&quot;: [
    &quot;template.html&quot;
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;We can also use the command component create progress-bar to create the component&apos;s structure.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Install dependencies&lt;/h3&gt;
&lt;p&gt;We will need two dependencies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/component/domify&quot;&gt;component/domify&lt;/a&gt;: to turn the HTML template into DOM elements.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/nk-components/dom-transform&quot;&gt;nk-components/dom-transform&lt;/a&gt;: to update the scaleX property value of the bar.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;$ component install component/domify nk-components/dom-transform
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Build the component&lt;/h3&gt;
&lt;p&gt;Component(1) module definition is based on CommonJS spec, like Node.js. You can find more information about CommonJS module &lt;a href=&quot;http://dailyjs.com/2010/10/18/modules/&quot;&gt;in this article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We need to define the logic and the UI of the component. Let&apos;s create &lt;code&gt;index.js&lt;/code&gt;, &lt;code&gt;progress-bar.css&lt;/code&gt; and &lt;code&gt;template.html&lt;/code&gt; as we specified in &lt;code&gt;component.json&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;index.js&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// require installed dependencies.
var domify = require(&apos;domify&apos;);
var transform = require(&apos;dom-transform&apos;);

// require local template.
// component-build(1) converts HTML template to string.
var template = require(&apos;./template.html&apos;);

// ProgressBar function will be accessible to others components.
module.exports = ProgressBar;

function ProgressBar() {
  // Convert string to DOM element
  this.el = domify(template);
  this.percentBar = this.el.querySelector(&apos;.js-percentBar&apos;);

  transform(this.percentBar, {
    scaleX: 0,
  });
}

ProgressBar.prototype.percent = function (value) {
  transform(this.percentBar, {
    scaleX: value,
  });
};
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;progress-bar.css&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;.progressBar {
  position: relative;
  width: 100%;
  height: 10px;
}

.progressBar-percent {
  width: 100%;
  height: 100%;
  background: red;
  -webkit-transform-origin: 0 0;
  transform-origin: 0 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;template.html&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;progressBar&quot;&amp;gt;
  &amp;lt;div class=&quot;progressBar-percent js-percentBar&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To be able to test the component we need to build it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ component build --dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;--dev&lt;/code&gt; option adds source map which is really helpful for debugging. You can find more options with &lt;code&gt;component build --help&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Component-build(1) converts HTML files to strings and JSON files to objects. You can require them directly inside a JavaScript module: &lt;code&gt;require(&apos;./template.html&apos;)&lt;/code&gt; and &lt;code&gt;require(&apos;./data.json&apos;)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;While developing a small component like this, I use &lt;a href=&quot;https://github.com/lepture/rewatch&quot;&gt;rewatch&lt;/a&gt; to execute the build command every time I save a file. There is no point in using Grunt or Gulp in that case.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ rewatch *.js *.css *.html -c &quot;component build --dev&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we add a demo page to see how it looks.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;demo.html&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;build/build.css&quot; /&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;script src=&quot;build/build.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script&amp;gt;
      var ProgressBar = require(&apos;progress-bar&apos;);

      var bar = new ProgressBar();
      document.body.appendChild(bar.el);

      var percent = 0;
      var intervalId = setInterval(function () {
        bar.percent(percent);
        percent += 0.2;

        if (percent &amp;gt; 1) {
          clearInterval(intervalId);
        }
      }, 300);
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Test your component&lt;/h3&gt;
&lt;p&gt;There are different possibilities to test a component: &lt;a href=&quot;http://visionmedia.github.io/mocha/#browser-support&quot;&gt;Mocha&lt;/a&gt;, &lt;a href=&quot;https://github.com/defunctzombie/zuul&quot;&gt;Zuul&lt;/a&gt;, &lt;a href=&quot;https://github.com/MatthewMueller/component-test&quot;&gt;component-test&lt;/a&gt;, ... And certainly others I don&apos;t know yet. I currently use component-test. It is simple to use and doesn&apos;t need configuration. You can run the test in the browser, PhantomJS or &lt;a href=&quot;https://saucelabs.com/&quot;&gt;Sauce Labs&lt;/a&gt;. If you want more information, the &lt;a href=&quot;https://github.com/MatthewMueller/component-test/blob/master/Readme.md&quot;&gt;readme&lt;/a&gt; is well documented.&lt;/p&gt;
&lt;p&gt;Whatever solution you prefer to use, don&apos;t forget to test your code.&lt;/p&gt;
&lt;p&gt;We can now push the code to GitHub and add the link to Component&apos;s wiki. Don&apos;t forget to add a tag (&lt;code&gt;git tag&lt;/code&gt;) for each &lt;a href=&quot;http://semver.org/&quot;&gt;version&lt;/a&gt; you release. Otherwise you won&apos;t be able to specify a version number when you use it as a dependency. Which is really dangerous, if one day you decide to change the component&apos;s API, it will break applications that use it. If you are lazy, you can still use &lt;a href=&quot;https://github.com/mikaelbr/mversion&quot;&gt;mversion&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Structuring an application&lt;/h2&gt;
&lt;p&gt;Let&apos;s pretend we want to develop an application which contains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Two pages: home and about.&lt;/li&gt;
&lt;li&gt;A script to load images.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;├── component.json
├── app
│   ├── boot
│   │   └── component.json
│   ├── lib
│   │   └── image-loader
│   │       └── component.json
│   └── pages
│       ├── about
│       │   └── component.json
│       └── home
│           └── component.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The file at the root of the project is the main &lt;code&gt;component.json&lt;/code&gt; of the application. This is where we will run the command to build the application.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;appName&quot;,
  &quot;paths&quot;: [
    &quot;app&quot;
  ],
  &quot;local&quot;: [
    &quot;boot&quot;
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;paths&lt;/code&gt; property is used by the builder to know where it has to look at to find &lt;code&gt;local&lt;/code&gt; dependencies. Here we specify &lt;code&gt;boot&lt;/code&gt;, the entry point of the application, located in &lt;code&gt;app/&lt;/code&gt; path.&lt;/p&gt;
&lt;p&gt;Each components define their own dependencies, local or published, therefore we don&apos;t need to add more information in this &lt;code&gt;component.json&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Local components&lt;/h2&gt;
&lt;h3&gt;Boot&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;boot&lt;/code&gt; component will be the first script executed in the application. It manages routing (&lt;code&gt;visionmedia/page.js&lt;/code&gt;) and transitions between each pages. It also defines the base of the interface, using &lt;a href=&quot;https://github.com/necolas/normalize.css/&quot;&gt;normalize.css&lt;/a&gt;, &lt;code&gt;global.css&lt;/code&gt; and &lt;code&gt;fonts.css&lt;/code&gt;. We add the pages components, home and about, as local dependencies.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;boot&quot;,
  &quot;dependencies&quot;: {
    &quot;necolas/normalize.css&quot;: &quot;*&quot;,
    &quot;visionmedia/page.js&quot;: &quot;*&quot;
  },
  &quot;paths&quot;: [
    &quot;../pages&quot;
  ],
  &quot;local&quot;: [
    &quot;home&quot;,
    &quot;about&quot;
  ],
  &quot;scripts&quot;: [
    &quot;index.js&quot;
  ],
  &quot;styles&quot;: [
    &quot;fonts.css&quot;,
    &quot;global.css&quot;
  ],
  &quot;fonts&quot;: [
    &quot;fonts/open-sans.eot&quot;,
    &quot;fonts/open-sans.svg&quot;,
    &quot;fonts/open-sans.ttf&quot;,
    &quot;fonts/open-sans.woff&quot;
  ],
  &quot;images&quot;: [
    &quot;images/background.jpg&quot;
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To use a static assets, like the background image, you just need to use relative file path. &lt;code&gt;component build&lt;/code&gt; symlinks those files to the build folder and replace the &lt;code&gt;url()&lt;/code&gt; values (except if you specify an absolute path).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;body {
  background-image: url(&apos;images/background.jpg&apos;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Pages&lt;/h3&gt;
&lt;p&gt;The pages use the local component to load images and &lt;code&gt;emitter&lt;/code&gt; to communicate with &lt;code&gt;boot&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;home&quot;,
  &quot;dependencies&quot;: {
    &quot;component/emitter&quot;: &quot;*&quot;
  },
  &quot;paths&quot;: [
    &quot;../../lib&quot;
  ],
  &quot;local&quot;: [
    &quot;image-loader&quot;
  ],
  &quot;scripts&quot;: [
    &quot;index.js&quot;
  ],
  &quot;styles&quot;: [
    &quot;home.css&quot;
  ],
  &quot;template&quot;: [
    &quot;template.html&quot;
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;I pushed an &lt;a href=&quot;https://github.com/kewah/component-example&quot;&gt;example&lt;/a&gt; on GitHub to show how an application could be organized. I also used Sass for the demo.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Sometimes you may wonder if you need to move a component to its own repository. Personally, when I&apos;m working on a project, I only creates local components. When the project ends, I extract components that can be useful for a new project. At North Kingdom we started to do that after the launch of &lt;a href=&quot;http://middle-earth.thehobbit.com/&quot;&gt;The Hobbit project&lt;/a&gt;. We share our components in &lt;a href=&quot;https://github.com/nk-components&quot;&gt;nk-components&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Custom builds&lt;/h2&gt;
&lt;p&gt;When it comes to build larger applications, you may want to use CSS preprocessor or template engines. Component-build(1) has &lt;code&gt;--use&lt;/code&gt; option, where you define the list of &lt;a href=&quot;https://github.com/component/component/wiki/Custom-builds&quot;&gt;plugins&lt;/a&gt; it has to execute.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ component build --use component-scss
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you don&apos;t want to use component-build(1) but prefer tasks runner like Grunt or Gulp, I suggest you to use &lt;a href=&quot;https://github.com/anthonyshort/grunt-component-build&quot;&gt;grunt-component-build&lt;/a&gt; or &lt;a href=&quot;https://github.com/yyx990803/gulp-component&quot;&gt;gulp-component&lt;/a&gt;. It is possible to create your own build script using &lt;a href=&quot;https://github.com/component/builder.js&quot;&gt;builder.js&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Building the application&lt;/h3&gt;
&lt;p&gt;To build the application you need to be at the root of the project (where the main &lt;code&gt;component.json&lt;/code&gt; is located) and run:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;component install&lt;/code&gt;: installs the dependencies defined by local components.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;component build&lt;/code&gt;: build the files that will be included into the index.html.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The final step is to create the &lt;code&gt;index.html&lt;/code&gt; that will be served to the user. Don&apos;t forget to require &lt;code&gt;boot&lt;/code&gt;, otherwise the application will never start.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;build/build.css&quot; /&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;script src=&quot;build/build.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script&amp;gt;
      require(&apos;boot&apos;);
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;My thoughts&lt;/h2&gt;
&lt;p&gt;I really like the way we organize applications with component(1), where each component is developed and lives separately in the project.&lt;/p&gt;
&lt;p&gt;At North Kingdom, we don&apos;t use a specific framework like Backbone or Angular. Each project has different constraints, some need to be connected to a database, some need to handle deep links... component(1) lets us to be flexible and just use what we really need. We find a lot of good resources published by the community and the number of shared components is still growing.&lt;/p&gt;
&lt;p&gt;Component(1) has some drawback like the file size. Aliases, at the bottom of &lt;code&gt;build.js&lt;/code&gt;, could take &amp;gt;10% of the file size. Which is a lot compared to other solution like browserify. Also, it may seem tedious to have to reference each of the dependencies and modules for each of the components. This is a design choice, but this could be simplified with a generator like Yeoman.&lt;/p&gt;
&lt;p&gt;I see it as a preview of the future of Web development. &lt;a href=&quot;http://www.w3.org/TR/2012/WD-components-intro-20120522/&quot;&gt;Web Components&lt;/a&gt; will be available in modern browsers in a few years with even more powerful features. The team behind component(1) plans to support Web Components and this is a great news.&lt;/p&gt;
&lt;p&gt;Further reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/component/spec&quot;&gt;component(1) spec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/component/component/wiki/F.A.Q&quot;&gt;component(1) F.A.Q&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/component/component/wiki/Commands&quot;&gt;Third-party component sub-commands&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>From Sublime Text to Vim</title><link>https://kewah.com/from-sublime-text-to-vim/</link><guid isPermaLink="true">https://kewah.com/from-sublime-text-to-vim/</guid><description>I explain the reasons that made me moved from Sublime Text to Vim has my main editor
</description><pubDate>Wed, 05 Feb 2014 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Sublime Text has been the first text editor I really enjoyed to use. Fast, simple to customize and lots of great plugins.&lt;/p&gt;
&lt;p&gt;Progressively, my habits have changed. At first, for instance, I used to rely on the UI to find a file and double click to open it. Then I discovered the fuzzy finder (&lt;code&gt;cmd + t&lt;/code&gt;) which helped me to switch quickly between files. A simple thing that improved my workflow a lot.&lt;/p&gt;
&lt;p&gt;Throughout my use of Sublime Text, I wanted to have a more comfortable experience, especially during code editing (selection, navigation, etc.), by reducing the back and forth between the keyboard and the mouse.&lt;/p&gt;
&lt;p&gt;I first looked into Sublime Text settings to try out some options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/kewah/sublime-text-config/blob/master/Default%20(OSX).sublime-keymap#L15-L18&quot;&gt;Expand selection to {quote, white space, brackets, indentation}&lt;/a&gt; that lets you select a portion of your code, inside quotes for example.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/kewah/sublime-text-config/blob/master/Default%20(OSX).sublime-keymap#L15-L18&quot;&gt;Jump to next/previous empty line&lt;/a&gt; which is useful when jumping from one block of code to another.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And of course there are several plugins that helped me. I particularly enjoy &lt;a href=&quot;https://github.com/tednaleid/sublime-EasyMotion&quot;&gt;Easy Motion&lt;/a&gt; &quot;that allows you to move the cursor to any character in your current view&quot;. It lets you navigate quickly inside your code, and for me, it is as important as the fuzzy finder. In fact, when I started to use this plugin, I wanted to do everything in that way. Being able to delete a text, starting from the position of the cursor to a defined character somewhere on my screen, would have been awesome.&lt;/p&gt;
&lt;p&gt;Unfortunately I did not find a plugin to fulfil my need.&lt;/p&gt;
&lt;h2&gt;The discovery of Vim&lt;/h2&gt;
&lt;p&gt;After some research, I decided to give Vim a try. In order to facilitate the transition, I started to use Vim as I did with Sublime Text, following Yehuda Katz advice. I installed MacVim and searched an equivalent for each of my favourite plugins:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/junegunn/vim-plug&quot;&gt;Vim-Plug&lt;/a&gt;: One of the Vim&apos;s &quot;package manager&quot;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Lokaltog/vim-easymotion&quot;&gt;EasyMotion&lt;/a&gt;: I already talked about it.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/kien/ctrlp.vim&quot;&gt;CTRL-P&lt;/a&gt;: Fuzzy finder.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/sirver/UltiSnips&quot;&gt;UltiSnips&lt;/a&gt;: Snippets for Vim&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mattn/emmet-vim/&quot;&gt;Emmet&lt;/a&gt;: Well, you know...&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/w0rp/ale&quot;&gt;Ale&lt;/a&gt;: Syntax checker.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/terryma/vim-multiple-cursors&quot;&gt;Multiple cursor&lt;/a&gt;: The little brother of the &quot;multiple cursors&quot; on Sublime Text.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/scrooloose/nerdtree&quot;&gt;NERD Tree&lt;/a&gt;: In case I need a file explorer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In parallel, I read the book &lt;a href=&quot;http://pragprog.com/book/dnvim/practical-vim&quot;&gt;Practical Vim&lt;/a&gt;, which allowed me to familiarize myself with the Vim commands and discover the range of possibilities they offer.&lt;/p&gt;
&lt;p&gt;Everyday, I set out a goal to focus on a specific command. It allows me not to overload my memory with a lot of new commands and not spend too much time looking for the right way to do it. With the intention of remaining productive in my work.&lt;/p&gt;
&lt;p&gt;Step by step I reduced my use of the mouse, disabled the arrow keys and finally started to &lt;a href=&quot;http://vimcasts.org/blog/2013/02/habit-breaking-habit-making/&quot;&gt;use more and more word wise motions&lt;/a&gt; to navigate inside my code.&lt;/p&gt;
&lt;h2&gt;Magic motions&lt;/h2&gt;
&lt;p&gt;In Vim, when you execute an operator command (copy, change, delete, select, etc.), you use motions to tell where you want the cursor to move.&lt;/p&gt;
&lt;p&gt;Motions are pretty simple to remember. For example &lt;code&gt;i&lt;/code&gt; mean &quot;inner&quot;, so if you want to delete the text inside the braces: &lt;code&gt;di}&lt;/code&gt;. This is very powerful and can be applied in different context, such as search keys (&lt;code&gt;f&lt;/code&gt; and &lt;code&gt;t&lt;/code&gt;). There is a really interesting article about &lt;a href=&quot;http://blog.carbonfive.com/2011/10/17/vim-text-objects-the-definitive-guide/&quot;&gt;text objects&lt;/a&gt; by Jared Carroll. Efficient editing in Vim is a matter of learning how to &lt;a href=&quot;http://yanpritzker.com/2011/12/16/learn-to-speak-vim-verbs-nouns-and-modifiers/&quot;&gt;speak proper Vim&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The use of motions is the first thing I truly enjoyed with Vim and that was exactly what I was looking for. That made me switch definitively to Vim.&lt;/p&gt;
&lt;h2&gt;6 months later&lt;/h2&gt;
&lt;p&gt;I&apos;ve mostly found everything I used to use in Sublime Text. However, I had to spend some hours to configure Vim. It is annoying to start with an empty shell but I&apos;ve found lots of &lt;code&gt;.vimrc&lt;/code&gt; on GitHub to used as an example.&lt;/p&gt;
&lt;p&gt;It might be sometimes difficult to achieve tasks that were relatively simple in Sublime Text. I had to learn new tools like &lt;a href=&quot;https://github.com/ggreer/the_silver_searcher&quot;&gt;The Silver Searcher&lt;/a&gt;, to be able to search and replace strings in my project.&lt;/p&gt;
&lt;p&gt;It is nevertheless really pleasant to edit my code in Vim. I still have a lot to learn but this is also something I like, I am always thrilled to discover new things and I never get bored.&lt;/p&gt;
&lt;p&gt;Some links that might be useful:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://pragprog.com/book/dnvim/practical-vim&quot;&gt;Practical Vim&lt;/a&gt;: My Vim&apos;s bible.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://vimcasts.org/&quot;&gt;Vimcasts&lt;/a&gt;: Screencasts by the same author of Practical Vim.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://dougblack.io/words/a-good-vimrc.html&quot;&gt;A good vimrc&lt;/a&gt;: Lot of informations to set up your vimrc.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://nerds.weddingpartyapp.com/tech/2013/11/17/mastering-vim-in-vim/&quot;&gt;Mastering Vim in Vim&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://vimgolf.com/&quot;&gt;VimGolf&lt;/a&gt;: Inspiring use of motions.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://vim-adventures.com/&quot;&gt;Vim Adventures&lt;/a&gt;: Learning Vim while playing a
game.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item></channel></rss>