<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Aymeric Beaumet</title>
    <description>The latest articles on DEV Community by Aymeric Beaumet (@aymericbeaumet).</description>
    <link>https://dev.to/aymericbeaumet</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F35208%2F2308cf49-7fdb-43db-8390-f509dff9ecc6.jpeg</url>
      <title>DEV Community: Aymeric Beaumet</title>
      <link>https://dev.to/aymericbeaumet</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aymericbeaumet"/>
    <language>en</language>
    <item>
      <title>Developers, please nurture your coding experience</title>
      <dc:creator>Aymeric Beaumet</dc:creator>
      <pubDate>Fri, 19 Aug 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/aymericbeaumet/developers-please-nurture-your-coding-experience-1l03</link>
      <guid>https://dev.to/aymericbeaumet/developers-please-nurture-your-coding-experience-1l03</guid>
      <description>

&lt;p&gt;&lt;em&gt;For a better reading experience, read the article where it was originally posted: &lt;a href="https://aymericbeaumet.com/developers-please-nurture-your-coding-experience"&gt;https://aymericbeaumet.com/developers-please-nurture-your-coding-experience&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I see writing code as an art. An art where we would each approach from a different perspective, playing with the tools we are being offered. We all evolve at a different level of the stack. And yet, we all conceptually work very similarly: editing some code in a form or another to make the machines behave as we expect.&lt;/p&gt;

&lt;p&gt;What is the only interface between you and the code you write? Your machine. Your workstation. Your work environment. Your workflow. There are different ways to name it, but ultimately that's what enabling you to write code.&lt;/p&gt;

&lt;p&gt;I believe taking complete ownership of one's environment is a key attribute of becoming a better engineer. Some people would argue it brings a productivity boost. While that might be true, I think the main reason to get to know your environment is to move potential obstacles out of the way. To let your thoughts flow efficiently from your brain to code. The productivity boost is a &lt;em&gt;consequence&lt;/em&gt;, not a mean in itself.&lt;/p&gt;

&lt;p&gt;We should all get to know our environments. Caring about it is not only beneficial for you. It is also beneficial for your coworkers and any person you work with. Not only that, but it will give you a better intuition of what represents a good or bad environment, and that always comes handy.&lt;/p&gt;

&lt;p&gt;The good news is: your journey has already begun. You already have a work environment. You already took inspiration from your peers. You are already using tools developed by people that happen to be improving their environment in a way that suits your needs. So where should you go next?&lt;/p&gt;

&lt;h2&gt;
  
  
  Pursue your journey
&lt;/h2&gt;

&lt;p&gt;I usually start by introspecting my workflow to identify pain points. It is often harder said than done, especially because our brain is excellent at &lt;em&gt;accepting&lt;/em&gt; a current situation as long as we've been exposed to it long enough. And that's still the case even if there are significant improvements waiting for us down the road.&lt;/p&gt;

&lt;p&gt;Introspection could happen in the form of self-assessing questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what are my most frequent tasks?&lt;/li&gt;
&lt;li&gt;where am I wasting my time?&lt;/li&gt;
&lt;li&gt;what are the most tedious tasks I have to do on a regular basis?&lt;/li&gt;
&lt;li&gt;which inconsistencies can I find in my workflow?&lt;/li&gt;
&lt;li&gt;what is slowing me down?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these questions will help you find potential directions for improvements.&lt;/p&gt;

&lt;p&gt;While working on this article, I was wondering about what would be the fundamental concepts if I were to distillate my mindset into key points. I found 5 of them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep your stack simple.&lt;/strong&gt; When you can, &lt;a href="https://www.youtube.com/watch?v=SxdOUGdseq4"&gt;choose simplicity over complexity&lt;/a&gt;. Find the smallest set of tools required to do the job and consolidate your workflow around them. Remove the tools you don't need.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don't let your stack get obsolete.&lt;/strong&gt; Challenge your existing stack on a regular basis. Make sure you keep your tools up-to-date. Do not miss out on the latest opportunities brought by modern tooling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integrate your tools together.&lt;/strong&gt; Having a small set of tools that you master and make them talk to each other is immensely useful. The &lt;a href="https://en.wikipedia.org/wiki/Unix_philosophy"&gt;Unix philosophy&lt;/a&gt; applies here. By design, it is easier to follow this principle when working in a terminal.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Practice is essential.&lt;/strong&gt; Getting to know your tooling is all down to practice. Learn the shortcuts. Learn how they behave. You need to deeply understand these tools, so they become an extension of yourself.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stability is key.&lt;/strong&gt; This is a never ending journey, and you should always keep digging for ways to improve. But also know when to pause. Your habits and muscular memory will be impacted if you change your stack too often.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Applying these concepts has been my rock over all these years. Sticking to this will slowly but steadily bring you to a better place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concrete examples
&lt;/h2&gt;

&lt;p&gt;I've started to code back in 2005. I was using Windows XP, Firefox, and &lt;a href="https://notepad-plus-plus.org/"&gt;Notepad++&lt;/a&gt; at the time. At that time the concept of productivity didn't even cross my mind. Over the years, I've gathered experiences in various environments (mainly Linux and macOS). I want to talk about a few situations which I've identified as problematic and improved in my workflow.&lt;/p&gt;

&lt;p&gt;The first one I want to mention is about my keyboard. &lt;em&gt;What is slowing me down?&lt;/em&gt; Well, I found myself doing a lot of typos when typing. I had to stop my flow, correct the error, and start typing again. So I looked it up and tried to find solutions. The first thing that comes up on the topic is to learn how to touch type (I couldn't recommend &lt;a href="https://www.keybr.com/"&gt;keybr.com&lt;/a&gt; enough). But then my brain was tickled: if I'm ready to spend time to improve my typing skills, is there more I could do? It turns out I could, and long story short, I ultimately settled on learning to touch type with the &lt;a href="https://colemak.com/"&gt;Colemak&lt;/a&gt; layout. It's been so since 2015, and I couldn't be happier.&lt;/p&gt;

&lt;p&gt;The second one is about the feedback loop when working on Go and Rust projects. &lt;em&gt;Where am I wasting my time?&lt;/em&gt; I was used to working with the Node.js ecosystem (&lt;a href="https://jestjs.io/"&gt;jest&lt;/a&gt;, &lt;a href="https://github.com/remy/nodemon"&gt;nodemon&lt;/a&gt;, &lt;a href="https://webpack.js.org/"&gt;webpack&lt;/a&gt;, etc) that natively integrates many ways to optimize the feedback loop (watching, reloading, hot-reloading, etc). I was manually restarting my programs and tests when working on Go and Rust projects, so I tried to find a way to replicate this. I have currently settled on using &lt;a href="https://github.com/watchexec/watchexec"&gt;watchexec&lt;/a&gt; that is a general purpose watcher. And I've &lt;a href="https://github.com/aymericbeaumet/dotfiles/blob/0d8dc7488ceae7262934ba17a5bf31b77a7264ff/.zshrc#L81"&gt;aliased it to &lt;code&gt;w&lt;/code&gt;&lt;/a&gt; to make it convenient to use.&lt;/p&gt;

&lt;p&gt;The third one is my shell. &lt;em&gt;What are my most frequent tasks?&lt;/em&gt; Well that is a software I'm using all day long, so I have high performance expectation. I want it to be &lt;em&gt;blazingly&lt;/em&gt; fast, and it was not the case. Startup time as well as printing the prompt was slow. A basic shell is actually fast by default. Just try for yourself: start zsh &lt;a href="https://unix.stackexchange.com/a/585012/38682"&gt;without loading the configuration files&lt;/a&gt; to see how fast it is. I didn't want to compromise on a rich prompt, but on the other hand, my shell was slow. So what to do? I began with an empty configuration file and only added the bits I needed. My &lt;a href="https://github.com/aymericbeaumet/dotfiles/blob/master/.zshrc"&gt;&lt;code&gt;~/.zshrc&lt;/code&gt;&lt;/a&gt; is 200 lines of code and loading 4 plugins. I have also made the switch to &lt;a href="https://github.com/romkatv/powerlevel10k"&gt;powerlevel10k&lt;/a&gt;. And I'm now satisfied with my shell user experience.&lt;/p&gt;

&lt;p&gt;The last one is a story happening at my current job. I'm working for &lt;a href="https://rekki.com"&gt;REKKI&lt;/a&gt; as a Lead Platform Engineer, and part of my team's mission is to design and improve the Developer Experience. In doing so, I'm letting the vision shared in this blog post infuse in my every day work. &lt;em&gt;What are the most tedious tasks I have to do on a regular basis?&lt;/em&gt; Well, setting up the stack for all the engineers working at REKKI was getting more and more painful. So we've released an &lt;a href="https://cli.rekki.team/"&gt;internal CLI&lt;/a&gt; that does this for us, installed by default on all the engineers machines.&lt;/p&gt;

&lt;p&gt;Each of those examples could be a blog post in itself. And I could continue over and over on other topics: learning how to debug and instrument my code, learning how not to use the mouse, learning how to master my editor (&lt;a href="https://github.com/aymericbeaumet/dotfiles/blob/master/.config/nvim/init.lua"&gt;nvim/init.lua&lt;/a&gt;), learning how to use snippets, learning how to take notes efficiently, learning how to create impactful slides, etc. But it's not the point.&lt;/p&gt;

&lt;p&gt;The point here is to show you the mindset: identify issues, find which one would have the biggest impact if fixed, and address them.&lt;/p&gt;

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

&lt;p&gt;We've seen why spending time on your work environment is important, along with some practical advices to help you shape it according to your own needs. We've also looked at some examples of how I applied these advices to improve my workflow over the past few years.&lt;/p&gt;

&lt;p&gt;This is in the end a very personal experience, and is all about getting to know yourself. Introspection is key here: what works for you might not work for someone else, and vice versa. This is &lt;em&gt;your&lt;/em&gt; journey, and I hope this post inspired you to revisit your work environment. I'd love to hear how you improved your workflow.&lt;/p&gt;

&lt;p&gt;Give it time, and you will be rewarded.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>workstations</category>
      <category>mindset</category>
    </item>
    <item>
      <title>Faster git clones with sparse checkouts</title>
      <dc:creator>Aymeric Beaumet</dc:creator>
      <pubDate>Sat, 23 Jul 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/aymericbeaumet/faster-git-clones-with-sparse-checkouts-1ah</link>
      <guid>https://dev.to/aymericbeaumet/faster-git-clones-with-sparse-checkouts-1ah</guid>
      <description>

&lt;p&gt;&lt;em&gt;For a better reading experience, read the article where it was originally posted: &lt;a href="https://aymericbeaumet.com/faster-git-clones-with-sparse-checkouts"&gt;https://aymericbeaumet.com/faster-git-clones-with-sparse-checkouts&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;We at &lt;a href="https://rekki.com"&gt;REKKI&lt;/a&gt; are working on a monorepo that contains all the backend Go code for most of our services and jobs. As time goes by and the size of this repository grows, the time it takes for an initial clone becomes noticeable. While this is not really a problem on the engineers' workstations as they operate statefully, it impacts stateless systems that have to download the codebase regularly: like CIs, CDs, or even our in-house &lt;a href="https://cli.rekki.team/"&gt;CLI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our CLI sometimes needs to pull a fresh version of a specific commit from the Go codebase to perform beta updates when our engineers request it (e.g.: to test new features that are not yet released in the stable version). Cloning the Go monorepo every time they want to install a beta update is not an option, as it takes more than a minute on a good connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shallow clones
&lt;/h2&gt;

&lt;p&gt;The first approach you often take in a situation like this is to shallow clone. While a default clone will fetch all the commits and all the &lt;a href="https://git-scm.com/book/en/v2/Git-Internals-Git-Objects"&gt;blob objects&lt;/a&gt; for the branch you track (by default &lt;code&gt;master&lt;/code&gt;/&lt;code&gt;main&lt;/code&gt;), a shallow clone will only fetch the blob objects for a subset of these commits, thus resulting in faster clone times.&lt;/p&gt;

&lt;p&gt;That is very common in the world of CIs. For example, both &lt;a href="https://github.com/actions/checkout"&gt;GitHub Actions&lt;/a&gt; and &lt;a href="https://docs.travis-ci.com/user/customizing-the-build/#git-clone-depth"&gt;Travis CI&lt;/a&gt; do it by default.&lt;/p&gt;

&lt;p&gt;One way you'd do a shallow clone is by specifying the &lt;code&gt;--depth=&amp;lt;n&amp;gt;&lt;/code&gt; flag to the &lt;code&gt;git clone&lt;/code&gt; command, hence limiting the number of commits to pull starting from the HEAD:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# fetch the latest commit&lt;/span&gt;
git clone &lt;span class="nt"&gt;--depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 git@github.com:rekki/go.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another way to shallow clone it is to specify a &lt;em&gt;start date&lt;/em&gt; instead of a number of commits. This is possible via the &lt;code&gt;--shallow-since=&amp;lt;date&amp;gt;&lt;/code&gt; flag with either an absolute or a relative date:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# fetch the last day of commits&lt;/span&gt;
git clone &lt;span class="nt"&gt;--shallow-since&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'1 day'&lt;/span&gt; git@github.com:rekki/go.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While shallow clones work well in some contexts, they fall short by downloading all the blob objects in the working tree for the given commits. So even specifying a depth of 1 results in non-negligible clone times for some repositories (in the case of our Go monorepo, this still takes 30 seconds).&lt;/p&gt;

&lt;h2&gt;
  
  
  Sparse checkouts
&lt;/h2&gt;

&lt;p&gt;Another way to reduce the amount of downloaded data is to use sparse checkouts. Sparse checkouts solve that problem by allowing to partially checkout a working tree. While shallow clones give you control over the &lt;em&gt;commits&lt;/em&gt; you want to fetch, sparse checkouts will enable you to specify the &lt;em&gt;blob objects&lt;/em&gt; you wish to fetch.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While sparse checkouts exist since git &lt;a href="https://github.com/git/git/blob/master/Documentation/RelNotes/2.25.0.txt"&gt;2.25.0&lt;/a&gt;, they are still considered experimental. It is unlikely that the feature will be removed in the future, but breaking changes might happen in both its implementation and its usage.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can either specify a list of directories (in what's called &lt;em&gt;cone mode&lt;/em&gt;) or a list of patterns (ala &lt;em&gt;.gitignore&lt;/em&gt;). The &lt;a href="https://git-scm.com/docs/git-sparse-checkout"&gt;manual&lt;/a&gt; recommends using the cone mode for performance reasons.&lt;/p&gt;

&lt;p&gt;Leveraging sparse checkouts allows you only to fetch the blob objects you need for the task you want to perform. In our case, we wanted only to clone the files required to build our CLI. This is how we did it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;blob:none &lt;span class="nt"&gt;--no-checkout&lt;/span&gt; git@github.com:rekki/go.git
&lt;span class="nb"&gt;cd &lt;/span&gt;go
git sparse-checkout &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--cone&lt;/span&gt; go.mod go.sum cmd/rekki-cli pkg/rekki/errors
git checkout master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's explain step by step:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;git clone&lt;/code&gt;: like a typical clone, but with 2 additional flags:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--filter=blob:none&lt;/code&gt;: instructs not to fetch any blob object&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--no-checkout&lt;/code&gt;: instructs not to automatically checkout &lt;em&gt;HEAD&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git sparse-checkout set&lt;/code&gt;: enables the sparse checkout settings and specifies which files should be checked out. Change this line to whatever works for you.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git checkout master&lt;/code&gt;: checkouts the actual branch or commit (in this case, the &lt;code&gt;master&lt;/code&gt; branch) and fetches the objects matching the sparse patterns.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can easily adapt these commands to your use cases.&lt;/p&gt;

&lt;p&gt;This approach allowed us to reduce the end-to-end time to clone to less than 5 seconds. We are satisfied with that number, and we know that time will only grow proportionally to the size of the code required to build the CLI (and nothing else). So it should stay fast for the foreseeable future.&lt;/p&gt;

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

&lt;p&gt;Using sparse checkouts is a bit more involved in the commands you have to execute, but it allows for more control over the files you download on your local filesystem. You don't always need the entirety of a git repository, and sparse checkouts allow you to pick what you need granularly.&lt;/p&gt;

&lt;p&gt;Give it a try the next time you find yourself in a situation where a git clone takes too long. What use case do you have in mind? Leave a comment with your ideas below.&lt;/p&gt;

</description>
      <category>git</category>
      <category>performance</category>
    </item>
    <item>
      <title>Prevent chromedp Chromium zombie processes from stacking</title>
      <dc:creator>Aymeric Beaumet</dc:creator>
      <pubDate>Sat, 13 Feb 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/aymericbeaumet/prevent-chromedp-chromium-zombie-processes-from-stacking-2221</link>
      <guid>https://dev.to/aymericbeaumet/prevent-chromedp-chromium-zombie-processes-from-stacking-2221</guid>
      <description>

&lt;p&gt;&lt;em&gt;For a better reading experience, read the article where it was originally posted: &lt;a href="https://aymericbeaumet.com/prevent-chromedp-chromium-zombie-processes-from-stacking"&gt;https://aymericbeaumet.com/prevent-chromedp-chromium-zombie-processes-from-stacking&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I have recently been playing with &lt;a href="https://github.com/chromedp/chromedp"&gt;chromedp&lt;/a&gt;. Chromedp is a Go library that enables you to control a Chromium browser programmatically. This can be used for different tasks: from testing to scraping. I have some memories of implementing a similar program in Node.js, and I was pleased to leverage a strongly typed language for the job.&lt;/p&gt;

&lt;p&gt;I was able to quickly start experimenting by following the &lt;a href="https://github.com/chromedp/examples"&gt;examples&lt;/a&gt; and browsing the &lt;a href="https://pkg.go.dev/github.com/chromedp/chromedp"&gt;documentation&lt;/a&gt;. For the record, this is what the chromedp hello world looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;chromedp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;chromedp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;chromedp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Navigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://github.com"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Not so long after I started getting results, I suddenly heard my MacBook Pro fans running crazy. Similar to what's happening when a &lt;code&gt;brew install&lt;/code&gt; is not going as expected, and you feel like you are recompiling gcc from scratch. This was accompanied by a weirdly unpleasant gentle warmth on my hands and an increasing slowness on the macOS UI.&lt;/p&gt;

&lt;p&gt;A quick look at the &lt;a href="https://aymericbeaumet.com/prevent-chromedp-chromium-zombie-processes-from-stacking/resources/chromium_zombies.png"&gt;activity monitor&lt;/a&gt; revealed that the Chromium processes were not terminated as expected. They were also using a lot of RAM, and forced my computer to swap (hence the perceived slowness).&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;pkill Chromium&lt;/code&gt; got me out of trouble. But now remains the question: how to prevent this from happening in the future? A quick search on Google led me to the chromedp issue tracker, were several &lt;a href="https://github.com/chromedp/chromedp/issues/81"&gt;resolved&lt;/a&gt; &lt;a href="https://github.com/chromedp/chromedp/issues/289"&gt;issues&lt;/a&gt; are reporting this very problem. They were all closed, and I didn't have the same symptoms, so I opened a &lt;a href="https://github.com/chromedp/chromedp/issues/752"&gt;new one&lt;/a&gt; to keep track of what I was facing.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;TLDR: if you are looking for the solution, jump to the end of this post.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The quick fix
&lt;/h2&gt;

&lt;p&gt;This problem will at some point be fixed upstream, but in the meantime I was looking for a temporary solution that would prevent the zombie processes from stacking (and eating my RAM). As it happens, the Chromium processes are spawned with the same user as the Go process. That means it should theoratically be possible (permission wise) to kill them from within the Go process right before it dies. I quickly tried to confirm this theory with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;chromedp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pkill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Chromium"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[warn] Failed to kill Chromium processes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;

    &lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is not as accurate as I would like it to be. All the Chromium processes on my machine (whose my user has rights on) would die, but this works. That being said, I'm not using Chromium as my primary browser, so I'm not really impacted. But then came the time to write this blog post, and I was sure at some point someone would face this issue &lt;em&gt;and&lt;/em&gt; would also be using Chromium as its primary browser. So I started digging.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going down the rabbit hole
&lt;/h2&gt;

&lt;p&gt;As it turns out, &lt;code&gt;man pkill&lt;/code&gt; gives us some interesting leads to explore. &lt;code&gt;pkill&lt;/code&gt; (and &lt;code&gt;pgrep&lt;/code&gt; for that matters, as they share most of their options) does support a flag allowing to restrict the processes receiving the signal to only the descendants of a specific PID (or said otherwise: all the processes whose parent is PID). This is achieved by providing the &lt;code&gt;-P &amp;lt;PID&amp;gt;&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;Once the code is adjusted to leverage the &lt;a href="https://golang.org/pkg/os/#Getpid"&gt;&lt;code&gt;os.Getpid()&lt;/code&gt;&lt;/a&gt; function, only the Chromium processes started by the Go process will be killed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pkill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-P"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Itoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getpid&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="s"&gt;"Chromium"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or so I thought, this doesn't work. As it turns out, the Chrome processes are not directly attached to the Go process as there are intermediate forks. So trying to match the parent process will not behave as you would expect.&lt;/p&gt;

&lt;p&gt;While looking at the activity monitor, I noticed there was something else than the process ID (PID) mentioned: the process group ID (PGID). I was not sure what this was about. A quick look at the &lt;a href="https://en.wikipedia.org/wiki/Process_group"&gt;Wikipedia page&lt;/a&gt; reads:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In a POSIX-conformant operating system, a process group denotes a collection of one or more processes. Among other things, a process group is used to control the distribution of a signal; when a signal is directed to a process group, the signal is delivered to each process that is a member of the group.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It felt like this was one way to go, so I decided to dig this whole process group concept. Reading &lt;code&gt;man ps&lt;/code&gt; helped me craft these two commands:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;ps -o pid,pgid,command&lt;/code&gt;: which lists all the processes on the system, along with their PID, PGID, and the full command&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ps -g &amp;lt;PGID&amp;gt;&lt;/code&gt;: which lists all the processes belonging to a specific process group ID&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By using the first command, I found my Go process PGID (&lt;em&gt;46499&lt;/em&gt;), and by using the second one I was able to list all of the processes in this group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ps &lt;span class="nt"&gt;-g&lt;/span&gt; 46499
  PID TTY           TIME CMD
46499 ttys001    0:00.77 go run &lt;span class="nb"&gt;.&lt;/span&gt;
46518 ttys001    0:38.43 /var/folders/dg/3zgc8tkd0wg7d0zc0mst6jc00000gn/T/go-build917574381/b001/exe/scraper
46520 ttys001    0:01.72 /Applications/Chromium.app/Contents/MacOS/Chromium &lt;span class="nt"&gt;--disable-popup-blocking&lt;/span&gt; &lt;span class="nt"&gt;--safebr&lt;/span&gt;
46521 ttys001    0:00.80 /Applications/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions
46522 ttys001    0:08.18 /Applications/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lists all the processes belonging to the Go process group (including Chromium's). We are getting close to what we want. As it turns out, &lt;code&gt;pkill&lt;/code&gt; also supports the &lt;code&gt;-g&lt;/code&gt; flag (with the same semantic). So changing the code as follows should behave as expected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pkill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-g"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Itoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getpid&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="s"&gt;"Chromium"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it doesn't. If you look carefully at the &lt;code&gt;ps&lt;/code&gt; command above, you can see the actual &lt;code&gt;scraper&lt;/code&gt; process, but above it is the &lt;code&gt;go run .&lt;/code&gt; process, which is the process that created the group. As we can see, the process group ID is the PID of the process that created the group. So we should be careful to use the &lt;em&gt;go run&lt;/em&gt; PID instead. In Go, you can get the PID of the parent process with &lt;a href="https://golang.org/pkg/os/#Getppid"&gt;os.Getppid()&lt;/a&gt;, which gives us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pkill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-g"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Itoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getppid&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="s"&gt;"Chromium"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Does it work? Yes, finally! Well, it works when we &lt;code&gt;go run .&lt;/code&gt;, but what happens when we &lt;code&gt;go build&lt;/code&gt; and directly run the binary? Well, it's not working. The reason is that the process group is no longer the one created by &lt;em&gt;go run&lt;/em&gt;, but the one created by the binary itself as we run it directly. So we need to account for that when we try to guess the PGID from the code.&lt;/p&gt;

&lt;p&gt;An easy solution is to try to &lt;code&gt;pkill&lt;/code&gt; both for the current pid &lt;em&gt;and&lt;/em&gt; the parent pid. At least one of them will be the process group ID. So as long as they don't both fail, it means we have succeeded to kill all the zombie processes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errA&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pkill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-g"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Itoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getppid&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="s"&gt;"Chromium"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errB&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pkill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-g"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Itoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getpid&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="s"&gt;"Chromium"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works. But I don't find this elegant as we expect half of the &lt;code&gt;pkill&lt;/code&gt; commands to fail during a normal execution process. Can we take it one step further? Let's have a look at &lt;code&gt;man pkill&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-g pgrp     Restrict matches to processes with a process group ID in the comma-separated list
            pgrp.  The value zero is taken to mean the process group ID of the running pgrep
            or pkill command.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reading this teaches us we could pass a comma-separated list of PID to &lt;code&gt;-g&lt;/code&gt;, which enables to only call &lt;em&gt;pkill&lt;/em&gt; once by passing both the PGID and the PID at the same time. But the second part of the description is much more interesting: by passing &lt;code&gt;-g 0&lt;/code&gt; we ask &lt;em&gt;pkill&lt;/em&gt; to infer for us what is the current process group ID. That greatly simplifies the code by moving this responsibility out of Go.&lt;/p&gt;

&lt;h3&gt;
  
  
  The solution
&lt;/h3&gt;

&lt;p&gt;By applying this to our piece of code, we reach this solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;chromedp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="c"&gt;// Prevent Chromium processes from hanging&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pkill"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-g"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Chromium"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[warn] Failed to kill Chromium processes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;

    &lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This does exactly what we want: it kills all the Chromium processes that have been started by the Go process without impacting any other Chromium process that would be running on your machine. This is simple, elegant, and works on all POSIX systems. It also has the advantage of being transposable to any other stack you use. Bingo!&lt;/p&gt;

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

&lt;p&gt;You might be wondering: was it worth digging all the way through considering you already had a working solution from the get-go? That's a good point. Well, first it was not working the way I expected it to. And second, I strongly believe our knowledge is always (and will always be) shallow, which makes me strive to take the time to dig deeper whenever I have the opportunity.&lt;/p&gt;

&lt;p&gt;It is enjoyable how sometimes a simple problem will challenge you on something you thought was well inside your comfort zone.&lt;/p&gt;

</description>
      <category>go</category>
      <category>chromedp</category>
      <category>chromium</category>
      <category>macos</category>
    </item>
    <item>
      <title>Behold z, the unsung jewel that rethinks shell navigation</title>
      <dc:creator>Aymeric Beaumet</dc:creator>
      <pubDate>Wed, 14 Feb 2018 00:00:00 +0000</pubDate>
      <link>https://dev.to/aymericbeaumet/behold-z-the-unsung-jewel-that-rethinks-shell-navigation-314a</link>
      <guid>https://dev.to/aymericbeaumet/behold-z-the-unsung-jewel-that-rethinks-shell-navigation-314a</guid>
      <description>

&lt;p&gt;&lt;em&gt;For a better reading experience, read the article where it was originally posted: &lt;a href="https://aymericbeaumet.com/behold-z-the-unsung-jewel-that-rethinks-shell-navigation"&gt;https://aymericbeaumet.com/behold-z-the-unsung-jewel-that-rethinks-shell-navigation&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I spend a fair amount of time wandering around in the terminal. The chances are that you are too since you dared to open this story. My daily activities led me to contribute to various kinds of projects (professional, personal and open-source). For my sanity, I tend to follow strict rules to organize my workspace, which helps me keep things tidied up and easily reachable. Or so I thought.&lt;/p&gt;

&lt;p&gt;I decided to arrange my projects by following the &lt;a href="https://golang.org/doc/code.html#Workspaces"&gt;Go philosophy&lt;/a&gt;: make the arborescence mirrors the urls where they are reachable. e.g., &lt;code&gt;~/Workspace/src/github.com/aymericbeaumet/dotfiles&lt;/code&gt; would contain the project &lt;a href="https://github.com/aymericbeaumet/dotfiles"&gt;dotfiles&lt;/a&gt; owned by &lt;a href="https://github.com/aymericbeaumet"&gt;@aymericbeaumet&lt;/a&gt; and accessible at &lt;a href="https://github.com"&gt;github.com&lt;/a&gt;. That's great, but I am now facing a complex directory structure tedious to navigate quickly. Making it, in the end, harder to access the resources I need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;
/Users/aymericbeaumet/Workspace/src/github.com/brigad/back-end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Nobody wants to manually deal with this.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Remembering and typing the location of each project (even while leveraging autocomplete) is time-consuming. Creating shell aliases is not scalable. Instead, I would like to rely on the pseudo-unicity of each project name to find it among all the directories on my computer.&lt;/p&gt;

&lt;h2&gt;
  
  
  z?
&lt;/h2&gt;

&lt;p&gt;Several tools exist to tackle this problem. My favorite is &lt;a href="https://github.com/rupa/z"&gt;&lt;em&gt;z&lt;/em&gt;&lt;/a&gt;. It follows the Unix philosophy by being small, focused and interoperable. &lt;em&gt;z&lt;/em&gt; allows jumping around swiftly, automatically learning from your habits. The entries are ranked based on the &lt;a href="https://en.wikipedia.org/wiki/Frecency"&gt;frecency&lt;/a&gt; at which you access the directories.&lt;/p&gt;

&lt;p&gt;Let’s walk through the basics of this tool. Below is how I advise to install it for Zsh on macOS (the install process should be similar for other shells and operating systems):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;z
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"source '&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;brew &lt;span class="nt"&gt;--prefix&lt;/span&gt; z&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/etc/profile.d/z.sh'"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.zshenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Install z for Zsh on macOS.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Note how installing &lt;em&gt;z&lt;/em&gt; is not enough to start using it. You have to source &lt;code&gt;z.sh&lt;/code&gt; at the beginning of each shell session to activate it. The &lt;code&gt;~/.zshenv&lt;/code&gt; configuration file is appropriate as Zsh &lt;a href="http://zsh.sourceforge.net/Intro/intro_3.html"&gt;loads it in most circumstances&lt;/a&gt;, making the tool accessible in a wide-variety of contexts (terminals, editors, IDEs, scripts, etc.).&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s play
&lt;/h2&gt;

&lt;p&gt;Once the install and setup are complete: start a new shell session, &lt;code&gt;cd&lt;/code&gt; through some folders and execute &lt;code&gt;z&lt;/code&gt; to confirm it has been accurately tracking your activity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;z | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-3&lt;/span&gt;
5331.72    /Users/aymericbeaumet/Workspace/src/github.com/brigad/back-end
637.255    /Users/aymericbeaumet/Workspace/src/github.com/brigad/ops
386.198 /Users/aymericbeaumet/.config/dotfiles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Top 3 directories based on frecency.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As shown above, I spend quite some time navigating through &lt;a href="https://github.com/brigad"&gt;Brigad&lt;/a&gt; projects, but it seems I also often access my &lt;a href="https://github.com/aymericbeaumet/dotfiles"&gt;dotfiles&lt;/a&gt; to tweak some configuration. The ranking score is volatile, and one could observe significant changes in a brief amount of time.&lt;/p&gt;

&lt;p&gt;Now that &lt;em&gt;z&lt;/em&gt; has accumulated some knowledge try it by navigating to one of the tracked folders. Navigating is as simple as executing the &lt;code&gt;z&lt;/code&gt; command followed by the directory name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;z dotfiles
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;
/Users/aymericbeaumet/.config/dotfiles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Navigating by matching with a whole directory name.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As a matter of facts, I do prefer to leverage partial matching (imagine &lt;code&gt;**&lt;/code&gt; surrounding your search pattern) to type fewer characters. Hence, &lt;code&gt;z dot&lt;/code&gt; is valid and should produce a similar behavior as &lt;code&gt;z dotfiles&lt;/code&gt; (modulo possible name collisions, but you get the idea). This works because &lt;em&gt;z&lt;/em&gt; relies on regular expressions. For example, the snippet below will navigate to the directory ending with the letters “tmp” (with the highest score):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;z tmp&lt;span class="err"&gt;$&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;
/private/tmp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Navigating by matching with a regular expression.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Neat right? I guess you start seeing the possibilities of this tool. And we have only scratched the surface of its capabilities. I suggest you dive in its &lt;a href="https://github.com/rupa/z/blob/master/README"&gt;documentation&lt;/a&gt; to get an idea of its full potential. Features such as autocomplete, subdirectories matching, consecutive matching, etc. are a game changer.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;z&lt;/em&gt; is tremendous. This tool saves me a non-trivial amount of time every single day. When one starts relying on it, there is no going back. And I hope it’s going to fit your workflow as well as mine.&lt;/p&gt;

&lt;p&gt;I would be grateful to hear any of your time-saving tricks. Feel free to share your experiences!&lt;/p&gt;

</description>
      <category>macos</category>
      <category>shell</category>
      <category>zsh</category>
    </item>
    <item>
      <title>Properly shim AngularJS applications using Browserify</title>
      <dc:creator>Aymeric Beaumet</dc:creator>
      <pubDate>Sun, 12 Oct 2014 00:00:00 +0000</pubDate>
      <link>https://dev.to/aymericbeaumet/properly-shim-angularjs-applications-using-browserify-1h59</link>
      <guid>https://dev.to/aymericbeaumet/properly-shim-angularjs-applications-using-browserify-1h59</guid>
      <description>

&lt;p&gt;&lt;em&gt;For a better reading experience, read the article where it was originally posted: &lt;a href="https://aymericbeaumet.com/properly-shim-angularjs-applications-using-browserify"&gt;https://aymericbeaumet.com/properly-shim-angularjs-applications-using-browserify&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Edit (March 5th 2015): the frontend community has evolved in the last few months and tends to be less hostile to the CommonsJS style (e.g.: Angular is now available on npm). This article has been rewritten accordingly.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://angularjs.org/"&gt;AngularJS&lt;/a&gt; is a frontend framework developed by folks at Google. It allows to build advanced web applications in a modular way by splitting the code base into small components. This brings a lot of advantages like a clean separation of concerns and an easier testability.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://browserify.org/"&gt;Browserify&lt;/a&gt; is a tool which allows to benefit from the &lt;a href="http://wiki.commonjs.org/wiki/CommonJS"&gt;CommonJS&lt;/a&gt; modules in theoretically any JavaScript environments. Given an entry point, it computes a dependency tree, resolves it and produces a single output file. By consuming the &lt;code&gt;package.json&lt;/code&gt;, Browserify enables to require &lt;code&gt;node_modules&lt;/code&gt; in the build. This allows to rely on &lt;a href="https://www.npmjs.com/"&gt;npm&lt;/a&gt; as a package manager for frontend dependencies, where &lt;a href="https://bower.io/"&gt;Bower&lt;/a&gt; or &lt;a href="http://component.github.io/"&gt;Component&lt;/a&gt; would have been usually used.&lt;/p&gt;

&lt;p&gt;When I first heard about Browserify, I immediately thought the modularity it brings would be really nice to build AngularJS applications. And it actually is. However they are not a perfect match by now, and some drawbacks need to be fixed.&lt;/p&gt;

&lt;p&gt;This article presents a solution to structure an AngularJS application using Browserify. It covers the use of non-CommonJS modules as dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  A sample application
&lt;/h2&gt;

&lt;p&gt;Let’s consider a sample application which sets an entry in the local storage, and uses it to react differently whether it’s the first time a user is loading the page. It will be used as a support to identify the trouble cases and detail how to tackle them. So create an empty folder and let’s get started!&lt;/p&gt;

&lt;p&gt;First, create an &lt;code&gt;app.js&lt;/code&gt; file which will be the entry point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;angular&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;angular&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;angular&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;angular-local-storage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]);&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;localStorageService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;localStorageService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lastVisit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;angular&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello stranger!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;angular&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome back!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;localStorageService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lastVisit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neat, isn’t it? If you come from a Node.js background I’m sure you do like it.&lt;/p&gt;

&lt;p&gt;Now let’s generate the &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; angular angular-local-storage jquery
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; browserify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then compile the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./node_modules/.bin/browserify app.js &lt;span class="nt"&gt;-o&lt;/span&gt; bundle.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally create a basic &lt;code&gt;index.html&lt;/code&gt; to load it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"bundle.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&lt;/span&gt; &lt;span class="na"&gt;ng-app=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty simple huh? But as you might expect, this doesn’t work. Open &lt;code&gt;index.html&lt;/code&gt; in a web browser and take a look at the console to find a nice JavaScript error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explanations
&lt;/h2&gt;

&lt;p&gt;There are several issues occurring here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modules not defining an entry point
&lt;/h3&gt;

&lt;p&gt;When a module is required, Browserify looks in the &lt;code&gt;node_modules&lt;/code&gt; folder to find its &lt;code&gt;package.json&lt;/code&gt; and loads it. Browserify expects it to have a &lt;code&gt;main&lt;/code&gt; property which contains the relative path to the file which should be loaded.&lt;/p&gt;

&lt;p&gt;It is usual that this property is missing, that it points to the wrong file or that the &lt;code&gt;package.json&lt;/code&gt; itself is missing. In such case, you can override the file to load in the own project’s &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"browser"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"angular"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./node_modules/angular/angular.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"angular-local-storage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./node_modules/angular-local-storage/dist/angular-local-storage.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Modules not properly exporting their content
&lt;/h3&gt;

&lt;p&gt;When Browserify requires the file indicated by the &lt;code&gt;main&lt;/code&gt; property in the module’s &lt;code&gt;package.json&lt;/code&gt;, it is usual that this file does not properly export its content in the CommonJS style. Also, we expect AngularJS modules to export their names so that we could directly require them in our AngularJS modules definitions.&lt;/p&gt;

&lt;p&gt;To this end, &lt;a href="https://github.com/thlorenz/browserify-shim"&gt;browserify-shim&lt;/a&gt; can be used.&lt;/p&gt;

&lt;p&gt;First install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; browserify-shim
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, update the &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"browserify-shim"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"angular"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"exports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"angular"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"angular-local-storage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"exports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"angular.module('LocalStorageModule').name"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"browserify"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"transform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"browserify-shim"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;angular&lt;/code&gt; exports the global variable &lt;code&gt;angular&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;angular-local-storage&lt;/code&gt; exports its AngularJS module name &lt;code&gt;LocalStorageModule&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Modules fetching their dependencies on the global context
&lt;/h3&gt;

&lt;p&gt;Some frontend modules relies on the fact their dependencies will be exposed on the global object (generally the &lt;code&gt;window&lt;/code&gt; object in most browsers). This is an anti-pattern of the CommonJS architecture which favors separation of concerns.&lt;/p&gt;

&lt;p&gt;For example, AngularJS expects jQuery to be exposed at the &lt;code&gt;window.jQuery&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;One solution is to wrap the module into a function which exposes a global object with the appropriate properties. Hopefully, this could also easily done via &lt;em&gt;browserify-shim&lt;/em&gt; (see above for the installation).&lt;/p&gt;

&lt;p&gt;Then, update the &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"browserify-shim"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"angular"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"depends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jquery:jQuery"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"angular-local-storage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"depends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"angular"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;angular&lt;/code&gt; will be provided the &lt;code&gt;jquery&lt;/code&gt; module on a fake window object at the property &lt;code&gt;jQuery&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;angular-local-storage&lt;/code&gt; will be provided the &lt;code&gt;angular&lt;/code&gt; module the same way&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;You should have added the following content to your &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"browser"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"angular"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./node_modules/angular/angular.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"angular-local-storage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./node_modules/angular-local-storage/dist/angular-local-storage.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"browserify-shim"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"angular"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"depends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jquery:jQuery"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"exports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"angular"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"angular-local-storage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"depends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"angular"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"exports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"angular.module('LocalStorageModule').name"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"browserify"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"transform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"browserify-shim"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you’ve made those corrections, you should be able to compile and run the sample application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./node_modules/.bin/browserify app.js &lt;span class="nt"&gt;-o&lt;/span&gt; bundle.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check it really works by opening &lt;code&gt;index.html&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A working example can be found &lt;a href="https://aymericbeaumet.com/properly-shim-angularjs-applications-using-browserify#attachments"&gt;attached to this post&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Side notes
&lt;/h2&gt;

&lt;p&gt;Some related facts which can save you time when dealing with Browserify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;modules not published to npm can be installed by giving their git repository URL: &lt;code&gt;npm install git+https://github.com/angular-ui/ui-router.git&lt;/code&gt;. Note that it is also possible to directly pass a GitHub repository: &lt;code&gt;npm install angular-ui/ui-router&lt;/code&gt;. A version can be specified by appending &lt;code&gt;#tag&lt;/code&gt;. See the &lt;a href="https://docs.npmjs.com/cli/install"&gt;documentation&lt;/a&gt; for more information&lt;/li&gt;
&lt;li&gt;modules not containing a &lt;code&gt;package.json&lt;/code&gt; cannot be installed using npm (even by directly providing a git repository). To tackle this issue, a third party installer like &lt;a href="https://github.com/shama/napa"&gt;napa&lt;/a&gt; has to be used&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;napa&lt;/code&gt; will directly install the dependencies in the &lt;code&gt;node_modules&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;postinstall&lt;/code&gt; script is equivalent to &lt;code&gt;install&lt;/code&gt;. It is executed after the modules installation. However, I find it to be more explicit&lt;/li&gt;
&lt;li&gt;you should place &lt;code&gt;napa&lt;/code&gt; into the &lt;code&gt;postinstall&lt;/code&gt; script&lt;/li&gt;
&lt;li&gt;the paths provided in the &lt;code&gt;browser&lt;/code&gt; field needs to be relative to the root &lt;code&gt;package.json&lt;/code&gt; path. It is mandatory to start each entry by &lt;code&gt;./&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;it is possible to indicate files in another repository than &lt;code&gt;node_modules&lt;/code&gt; when filling the browser field (e.g.: &lt;code&gt;bower_components&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;centralizing Browserify options in the &lt;code&gt;package.json&lt;/code&gt; is a good practice since it allows to keep a constant behavior when invoking it in different ways (CLI, API)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Even though there is a little more code to write to setup a project, this solution provides a beautiful way to organize its code in logical bricks. It totally matches the way AngularJS has been thought.&lt;/p&gt;

&lt;p&gt;Also, managing all the dependencies in a single place is really useful. Especially when developing full-stack JavaScript applications, hence potentially sharing dependencies between the frontend and the backend (e.g.: Async.js, Bluebird, Moment.js, highlight.js, …). Using a CommonJS loader allows to rely on the exact same versions of the modules both client-side and server-side.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>browserify</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
