<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[SumUp Engineering - Medium]]></title>
        <description><![CDATA[We’re the engineering team at SumUp, building the world’s leading payments and mPOS platform. Based in Berlin, Cologne, Sofia, and São Paulo. - Medium]]></description>
        <link>https://medium.com/sumup-engineering?source=rss----bc8d70b3a259---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>SumUp Engineering - Medium</title>
            <link>https://medium.com/sumup-engineering?source=rss----bc8d70b3a259---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 11 May 2026 16:53:20 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/sumup-engineering" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[10 lessons learned building SumUp’s design system]]></title>
            <link>https://medium.com/sumup-engineering/10-lessons-learned-building-sumups-design-system-577af292ae56?source=rss----bc8d70b3a259---4</link>
            <guid isPermaLink="false">https://medium.com/p/577af292ae56</guid>
            <category><![CDATA[design]]></category>
            <category><![CDATA[design-systems]]></category>
            <category><![CDATA[front-end-development]]></category>
            <dc:creator><![CDATA[Monica Lent]]></dc:creator>
            <pubDate>Thu, 15 Aug 2019 09:14:19 GMT</pubDate>
            <atom:updated>2019-08-15T09:27:32.043Z</atom:updated>
            <content:encoded><![CDATA[<p>How hard can it be?</p><p>So many teams start out thinking a design system will help solve a lot of the challenges they face while scaling development and design teams: consistency, efficiency, re-inventing the wheel.</p><p>While a design system is a modern and scalable way to address these kinds of issues, <strong>there are a lot of lessons to be learned</strong> from what introducing a design system looks like on paper — and what it looks like in production.</p><p>We started working on our design system at SumUp in January 2018.</p><p>Our goal was to introduce a scalable way to harmonize the experience across multiple frontend applications and amplify developer and designer productivity.</p><p>…all while simultaneously performing <em>both</em> a rebranding <em>and</em> a technical migration from Angular to React.</p><p><strong>It was an ambitious project.</strong></p><p>In the meantime, our team grew from 3 to over 15 frontend engineers and the number of cross-functional teams using the system multiplied as well.</p><p>Over a year and a half later, we finally feel ready to label our design system, called <a href="https://circuit.sumup.com"><strong>Circuit UI</strong></a>, version 1.0. We still have a lot of work ahead of us, and big plans for version 2.0, but we’ve also learned a lot along the way that we think is worth sharing.</p><p>Here are the <strong>key learnings from some of the designers and developers</strong> who helped us reach this important milestone.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OEgCGgrT7EiDVifxMFPoLw.png" /></figure><h3><strong>1. “Making the design system part of your business requirements is key”</strong></h3><p>My biggest learning is it’s extremely hard to balance the amount of effort you can put into the design system versus feature development. Most of the time when building such a solution, you still want to actively work on new features, not totally halt development. This is especially true if you are doing this while being part of a team that builds product features, and not part of a dedicated “Design System” initiative.</p><p>My advice is to <strong>avoid handling the design system as a side project</strong>, because it’s important for people to understand that a design system is a constant part of product development too. By ensuring everyone is on board and understands the value of such a tool, it’s way easier to push the design system forward in smaller increments, and even fit it into your regular release/delivery cycles, such as a sprint.</p><p>This is one of the secrets for a healthy growth.</p><p>— Fernando (<a href="https://twitter.com/fernando_fleury">@fernando_fleury</a>)</p><h3><strong>2. “Take the time to get it right”</strong></h3><p>One of the things I enjoyed the most about building Circuit UI was the opportunity to do in-depth research for each component. I learned so much about obscure HTML elements, weird CSS quirks, best practices for accessibility, and good API design. Now when we use the components in an application, we can feel confident that the component displays consistently across browsers, is keyboard and screen-reader accessible, and generally works well.</p><p>However, <strong>building these benefits into the component library is often not enough</strong>. Let’s take accessibility as an example, as it’s a topic that is particularly close to my heart. Often a user of the component library has to take extra steps to ensure that components remain accessible when integrated together in an application. A good component library makes it easy to do the right thing through good API design and education in documentation.</p><p>When you build your own component library, make sure you take the time to get it right. The initial investment might seem high, but it will pay off manifold over time.</p><p>— Connor (<a href="https://twitter.com/connor_baer">@connor_baer</a>)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OsmyUcmN_yUnLSybK-Mgaw.png" /></figure><h3><strong>3. “Creating the design system is only the beginning”</strong></h3><p>Probably my biggest take away from building out our design system is that creating the system itself is only the first step. The harder part is actually applying it to an existing codebases, especially if the design system is also introducing a redesign <em>and </em>changing frameworks like it was for us. That makes it more challenging to adopt gradually, and slowed down the process a lot.</p><p>On the flip side, the benefit was that when we finished, we had a very clean setup in the application: both on the technical side and the UI side. <strong>Introducing a design system was an effective and systematic way</strong> for us to revamp our biggest application from top to bottom.</p><p>— Monica (<a href="https://twitter.com/monicalent">@monicalent</a>)</p><h3><strong>4. “Deciding what belongs to the design system can be challenging”</strong></h3><p>Being a part of a product company means constantly working on new and exciting features. This inevitably leads to teams reusing existing components of the design system or building new ones. During feature development we were sometimes struggling with deciding if the super cool component we are implementing should live in Circuit UI or not.</p><p>My takeaway is that when it comes to this, it is really important to communicate with the rest of the team and involve people from design as soon as possible to <strong>assess if the component could turn into a UI pattern</strong> in your design system.</p><p>Of course, this could slow you down and be a bit stressful, especially if you are on a deadline.</p><p>But at the end of the day, UI consistency is a key factor in product development so you should do your best to help people from business and other stakeholders understand that investing more time in making a component generic could be really helpful in the future.</p><p>— Mariela (<a href="https://twitter.com/marielakas">@marielakas</a>)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iJEBdfQrBHimZ94MvKYAcg.png" /></figure><h3><strong>5. “Having a design system will not solve all your consistency problems”</strong></h3><p>One of the most direct benefits of having a design system is to gain consistency throughout the user experience. However, during Circuit UI’s development, we still experienced some of the same issues that justified its creation in the first place.</p><p>For instance, having two developers/designers working on the same new component or discovering areas that weren’t originally covered by the design system (one example from our side is iconography).</p><p>This led us to reflect that <strong>consistency is a never-ending mission</strong>. We need to continuously push our behavior and shape processes that allow visibility and ensure that we are pushing the best possible experience for us, as designers and developers, and for our merchants.</p><p>— João (<a href="https://twitter.com/joaovictortg">@joaovictortg</a>)</p><h3><strong>6. “Efficiency is the most tangible benefit of the design system approach”</strong></h3><p>After our design system was available as a beta version, I started to create a new application with Circuit UI and this opportunity showed me the benefits of having it in our lives.</p><p>For example, our product designer was using only the components of the design system and this meant he could create the first prototypes of our application very quickly.</p><p>When I started the implementation, I was able to worry more about the business logic than making sure all components will have the right CSS. In one day, we were able to build our first “login” screen and deploy it. The only thing I needed to think about was how to make the page interact with existing components.</p><p>This opportunity makes everyone feels more confident about the components and <strong>focus more on about the value we deliver</strong> to our merchants. Of course, sometimes we find out we need new components in Circuit UI because of new cases that appear in our new project!</p><p>And this is related to our first learning: we need to start working on our requirements and consider creating new components for Circuit UI (and not only our project) so we can enable more people to benefit from the new components we create.</p><p>— Herbert (<a href="https://twitter.com/herberth3nrique">@herberth3nrique</a>)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vNjHwU1mjhIBdWysGL8sVw.png" /></figure><h3><strong>7. “The strong sense of community made us stronger”</strong></h3><p>One of the greatest things about working on Circuit UI was the collaboration with great designers and developers from all over the world. In the beginning, I couldn’t even imagine the number of lessons and knowledge I would gather from people with different backgrounds, expertise, ideas, and styles.</p><p>Circuit UI became much more than a design system, <strong>it became a community </strong>where we could share experiences, new ideas, learn from each other and become better engineers and designers.</p><p>I truly believe that our team became stronger when working together on something that would benefit not just us, but the whole community.</p><p>— Afonso (<a href="https://twitter.com/kdsopasir">@kdsopasir</a>)</p><h3><strong>8. “Design systems are the living, breathing part of the collaboration between design and development”</strong></h3><p>You want a fig tree. You buy some seeds and you put a lot of care into making that baby grow. After weeks and months (How long do fig trees take?) of TLC you have your beautiful, leafy child and you’re proud as punch. Then you ignore it. You start growing new plants that need more attention and you forget about that once beautiful fig tree in the corner of the room.</p><p>The fig tree is your design system.</p><p>Our job, as designers &amp; developers, is to keep the design system proper, healthy and up to date. It’s not a full time job (yet) but it takes meaningful time and effort to maintain the dream of a consistent experience throughout our platforms.</p><p>— Matt (<a href="https://twitter.com/majahoughton">@majahoughton</a>)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rLIllQzvdEzSZ9SXohe0lg.png" /></figure><h3><strong>9. “Onboard new comers to the design system as soon as possible”</strong></h3><p>As more developers join our team, it is important for the new team members to spend time familiarizing themselves with the design system in detail and getting to know its capabilities and limitations from the start. Having this tool in your pocket can be a huge timesaver and <strong>being able to use it with certainty makes developers more productive</strong>.</p><p>To facilitate the “getting to know the design system” phase, well-written, up-to-date and detailed documentation certainly is a plus. Communicating clearly when a change is about to be implemented is also a big part of it.</p><p>Having an open discussion about potential improvements helps get everyone involved and invested in the project.</p><p>— Iva</p><h3><strong>10. “Even the biggest changes can and should be delivered in increments”</strong></h3><p>We started work on Circuit UI because we knew we needed a more consistent way to design and deliver our products. A design system and an implementation in React seemed like a great fit. We also knew we were a team of three frontend engineers in all of SumUp at the time. So how do you create a component library from scratch and rewrite the entire frontend using it? Short answer: you do not, at least not with a team this size.</p><p>But what do you do instead?</p><p>You come up with a plan to do it incrementally.</p><p>We started with a limited set of components, only what we knew we would need. The rest was added as we rewrote or created other products. We had a simple bash script that would generate some boilerplate files so creating new components would be quick. Nowadays we have a complete CLI with support for different types of components. When we started using Circuit UI in our web products we installed it as a dependency from GitHub. A build was added much later. Circuit UI still does not have working static CSS extraction. We only built a proof of concept because we knew we might need it down the road.</p><p>The point here is: if you want to make big changes with a small team, <strong>you have to find ways to do so incrementally</strong> so people can continue quickly and continuously deliver value to users.</p><p>The amazing thing I learned over the last year is that this actually works. It feels slow. Sometimes it is painful because you know you could do so much more.</p><p>But you just put one foot in front of the other and after some time, when looking back, <strong>you realize you have made it incredibly far</strong>.</p><p>— Felix (<a href="https://twitter.com/feju">@feju</a>)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Vd9tdnv9TgbNBfVvLTLu2A.png" /></figure><h3>Does working on Circuit UI sound like something you’d enjoy?</h3><p>We’re hiring both engineers who will build and maintain our design system as part of our new Web Platform team, as well as product designers who will use and extend the system.</p><p><strong>Check out our open roles for:</strong></p><ul><li><a href="https://sumup.com/careers/positions/4348627002/">Web Architect</a> (Berlin)</li><li><a href="https://sumup.com/careers/positions/4334555002/">Senior Frontend Engineers (Web Platform)</a> (Berlin)</li><li>Product Designers (<a href="https://sumup.com/careers/positions/4300456002/">São Paulo</a>, <a href="https://sumup.com/careers/positions/4193656002/">Berlin</a>, <a href="https://sumup.com/careers/positions/4113984002/">Sofia</a>)</li></ul><p>Follow us on twitter <a href="https://twitter.com/sumupeng">@SumUpEng</a> for future updates!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=577af292ae56" width="1" height="1" alt=""><hr><p><a href="https://medium.com/sumup-engineering/10-lessons-learned-building-sumups-design-system-577af292ae56">10 lessons learned building SumUp’s design system</a> was originally published in <a href="https://medium.com/sumup-engineering">SumUp Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Image creation and testing with HashiCorp Packer and ServerSpec]]></title>
            <link>https://medium.com/sumup-engineering/image-creation-and-testing-with-hashicorp-packer-and-serverspec-bb2bd065441?source=rss----bc8d70b3a259---4</link>
            <guid isPermaLink="false">https://medium.com/p/bb2bd065441</guid>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[infrastructure]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[linux]]></category>
            <category><![CDATA[hashicorp]]></category>
            <dc:creator><![CDATA[Anton Antonov]]></dc:creator>
            <pubDate>Sat, 01 Dec 2018 15:07:51 GMT</pubDate>
            <atom:updated>2019-04-12T22:11:25.762Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/737/1*05ESIZBKYzWebjSH_HOvSA.png" /><figcaption>Using HashiCorp Packer and ServerSpec</figcaption></figure><h3>Intro</h3><p>At <a href="http://github.com/sumup">SumUp</a> we use various technologies and tools depending on their purpose and trade-offs.</p><p>Recently we started a few infrastructure-heavy projects.</p><p>We had to solve the problem of creating images (virtual appliances) for QEMU and VirtualBox for multiple projects:</p><ul><li>one project creates guests using a chain of QEMU images</li><li>one project creates guest using a chain of VirtualBox images</li></ul><h3>What this article is going to cover as “hands-on” tech</h3><ul><li><a href="https://www.packer.io/">HashiCorp Packer</a> (v1.3.x)</li><li><a href="https://github.com/mizzy/serverspec">ServerSpec</a> (<a href="https://github.com/mizzy/serverspec/releases/tag/v2.41.3">v2.41.</a>x)</li></ul><h3>Source code:</h3><p><a href="https://github.com/syndbg/sumup-blog-hashicorp-packer-test">syndbg/sumup-blog-hashicorp-packer-test</a></p><h3>First, why Packer and what are the use-cases?</h3><p>Best check out the “Why” and “Use Cases” at <a href="https://www.packer.io/intro/why.html">https://www.packer.io/intro/why.html</a> and <a href="https://www.packer.io/intro/use-cases.html">https://www.packer.io/intro/use-cases.html</a></p><h3>What is ServerSpec and what are the use-cases?</h3><p>ServerSpec in a sense is a library with its own command runner and “resources” provider SpecInfra, all based around RSpec, the Ruby BDD test suite framework.</p><p>Sample code from <a href="https://serverspec.org/">https://serverspec.org/</a></p><pre>require &#39;spec_helper&#39;  </pre><pre>describe package(&#39;httpd&#39;), :if =&gt; os[:family] == &#39;redhat&#39; <strong>do</strong> <br>  it { should be_installed } <br><strong>end</strong></pre><p>It’s not the only test library and runner around RSpec, but it’s one with good popularity and there are good amount of resources to get your goal done.</p><p>One of the competition is <a href="https://www.inspec.io/">https://www.inspec.io/</a> by Chef. It almost reads the same and provides the same resources, since there’s not much room for metaphors and lingo when you’re talking about infrastructure.</p><p>Another sample code, but this time using InSpec (you can see more about the packageresource at <a href="https://www.inspec.io/docs/reference/resources/package/">https://www.inspec.io/docs/reference/resources/package/</a>)</p><pre>require &#39;spec_helper&#39;</pre><pre>describe package(&#39;httpd&#39;), :if =&gt; os[:family] == &#39;redhat&#39; <strong>do</strong> <br>  it { should be_installed } <br><strong>end</strong></pre><p>As you can see they’re *can* be exactly the same at times.</p><h3>What the ordinary Packer build flow looks like</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OPfLAFsZKtAuDxZhveDm2g.jpeg" /><figcaption>Note: communicator(s) and post-processor(s) have been omitted/greatly simplified for readability</figcaption></figure><h3>What the Packer build flow will look like with tests</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*K7GKDaaQBLMI1g_ybHRqsQ.jpeg" /><figcaption>Note: communicator(s) and post-processor(s) have been omitted/greatly simplified for readability</figcaption></figure><h3>Preparing the HashiCorp Packer Debian image to build:</h3><p>The full file looks like this: (we’re reviewing below specific parts what they give us as functionality) (ref: <a href="https://github.com/syndbg/sumup-blog-hashicorp-packer-test/blob/master/packer-base-debian.json">https://github.com/syndbg/sumup-blog-hashicorp-packer-test/blob/master/packer-base-debian.json</a>)</p><pre>{<br>  &quot;variables&quot;: {<br>    &quot;debian_version&quot;: &quot;9.6.0&quot;,<br>    &quot;boot_wait&quot;: &quot;5s&quot;,<br>    &quot;cpus&quot;: &quot;1&quot;,<br>    &quot;disk_size&quot;: &quot;5000&quot;,<br>    &quot;headless&quot;: &quot;false&quot;,<br>    &quot;memory&quot;: &quot;512&quot;,<br>    &quot;mirror&quot;: &quot;http://mirror.host.ag/debian/debian-cd/&quot;,<br>    &quot;iso_checksum_url&quot;: &quot;http://mirror.host.ag/debian/debian-cd/9.6.0/amd64/iso-cd/SHA512SUMS&quot;,<br>    &quot;ssh_timeout&quot;: &quot;30m&quot;,<br>    &quot;host_adapter&quot;: <em>null</em>,<br>    &quot;rdp_bind_address&quot;: &quot;127.0.0.1&quot;,<br>    &quot;must_run_tests&quot;: &quot;false&quot;,<br>    &quot;must_sleep_before_tests&quot;: &quot;false&quot;<br>  },<br>  &quot;provisioners&quot;: [<br>    {<br>      &quot;type&quot;: &quot;shell&quot;,<br>      &quot;scripts&quot;: [<br>        &quot;./files/base-debian/00_guest_additions.sh&quot;<br>      ],<br>      &quot;only&quot;: [&quot;virtualbox-base-debian&quot;]<br>    },<br>    {<br>      &quot;type&quot;: &quot;shell&quot;,<br>      &quot;scripts&quot;: [<br>        &quot;./files/base-debian/00_qemu_agent.sh&quot;<br>      ],<br>      &quot;only&quot;: [&quot;qemu-base-debian&quot;]<br>    },<br>    {<br>      &quot;type&quot;: &quot;shell&quot;,<br>      &quot;scripts&quot;: [<br>        &quot;./files/base-debian/01_ssh.sh&quot;<br>      ]<br>    },<br>    {<br>      &quot;type&quot;: &quot;shell&quot;,<br>      &quot;scripts&quot;: [<br>        &quot;./files/base-debian/02_disable_lvmetad.sh&quot;<br>      ]<br>    },<br>    {<br>      &quot;type&quot;: &quot;shell&quot;,<br>      &quot;scripts&quot;: [<br>        &quot;./files/base-debian/03_logging.sh&quot;<br>      ]<br>    },<br>    {<br>      &quot;type&quot;: &quot;shell&quot;,<br>      &quot;scripts&quot;: [<br>        &quot;./files/cleanup.sh&quot;<br>      ]<br>    },<br>    {<br>      &quot;type&quot;: &quot;shell-local&quot;,<br>      &quot;script&quot;: &quot;./serverspec/sleep_before_tests_packer.sh&quot;,<br>      &quot;environment_vars&quot;: [<br>        &quot;MUST_SLEEP_BEFORE_TESTS={{ user `must_sleep_before_tests` }}&quot;<br>      ]<br>    },<br>    {<br>      &quot;type&quot;: &quot;shell-local&quot;,<br>      &quot;command&quot;: &quot;./serverspec/run_packer.sh ./spec/base_debian_spec.rb&quot;,<br>      &quot;environment_vars&quot;: [<br>        &quot;MUST_RUN_TESTS={{ user `must_run_tests` }}&quot;,<br>        &quot;SUDO_PASSWORD=packer123&quot;,<br>        &quot;TARGET_PASSWORD=packer123&quot;,<br>        &quot;TARGET_USER=root&quot;,<br>        &quot;BUILDER_TYPE={{ build_type }}&quot;<br>      ]<br>    }<br>  ],<br>  &quot;builders&quot;: [<br>    {<br>      &quot;name&quot;: &quot;virtualbox-base-debian&quot;,<br>      &quot;type&quot;: &quot;virtualbox-iso&quot;,<br>      &quot;boot_command&quot;: [<br>        &quot;&lt;esc&gt;&lt;wait&gt;&quot;,<br>        &quot;install &lt;wait&gt;&quot;,<br>        &quot;preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg &lt;wait&gt;&quot;,<br>        &quot;DEBCONF_DEBUG=5 &lt;wait&gt;&quot;,<br>        &quot;debian-installer=en_US &lt;wait&gt;&quot;,<br>        &quot;auto=true &lt;wait&gt;&quot;,<br>        &quot;priority=high &lt;wait&gt;&quot;,<br>        &quot;locale=en_US &lt;wait&gt;&quot;,<br>        &quot;kbd-chooser/method=us &lt;wait&gt;&quot;,<br>        &quot;keyboard-configuration/xkb-keymap=us &lt;wait&gt;&quot;,<br>        &quot;netcfg/get_hostname=debian &lt;wait&gt;&quot;,<br>        &quot;netcfg/get_domain=  &lt;wait&gt;&quot;,<br>        &quot;netcfg/choose_interface=auto  &lt;wait&gt;&quot;,<br>        &quot;fb=false &lt;wait&gt;&quot;,<br>        &quot;debconf/frontend=noninteractive &lt;wait&gt;&quot;,<br>        &quot;console-setup/ask_detect=false &lt;wait&gt;&quot;,<br>        &quot;console-keymaps-at/keymap=us &lt;wait&gt;&quot;,<br>        &quot;&lt;enter&gt;&lt;wait&gt;&quot;<br>      ],<br>      &quot;boot_wait&quot;: &quot;{{ user `boot_wait` }}&quot;,<br>      &quot;disk_size&quot;: &quot;{{ user `disk_size` }}&quot;,<br>      &quot;format&quot;: &quot;ova&quot;,<br>      &quot;guest_additions_path&quot;: &quot;VBoxGuestAdditions_{{.Version}}.iso&quot;,<br>      &quot;guest_os_type&quot;: &quot;Debian_64&quot;,<br>      &quot;headless&quot;: &quot;{{ user `headless` }}&quot;,<br>      &quot;http_directory&quot;: &quot;http&quot;,<br>      &quot;iso_checksum_type&quot;: &quot;sha512&quot;,<br>      &quot;iso_checksum_url&quot;: &quot;{{ user `iso_checksum_url` }}&quot;,<br>      &quot;iso_urls&quot;: [<br>        &quot;iso/debian-{{ user `debian_version` }}-amd64-netinst.iso&quot;,<br>        &quot;{{ user `mirror` }}/{{ user `debian_version` }}/amd64/iso-cd/debian-{{ user `debian_version` }}-amd64-netinst.iso&quot;<br>      ],<br>      &quot;output_directory&quot;: &quot;output-{{ build_name }}&quot;,<br>      &quot;shutdown_command&quot;: &quot;systemctl poweroff&quot;,<br>      &quot;ssh_password&quot;: &quot;packer123&quot;,<br>      &quot;ssh_timeout&quot;: &quot;{{ user `ssh_timeout` }}&quot;,<br>      &quot;ssh_username&quot;: &quot;root&quot;,<br>      &quot;virtualbox_version_file&quot;: &quot;.vbox_version&quot;,<br>      &quot;vrdp_bind_address&quot;: &quot;{{ user `rdp_bind_address` }}&quot;,<br>      &quot;vboxmanage&quot;: [<br>        [<br>          &quot;modifyvm&quot;,<br>          &quot;{{ .Name }}&quot;,<br>          &quot;--memory&quot;,<br>          &quot;{{ user `memory` }}&quot;<br>        ],<br>        [<br>          &quot;modifyvm&quot;,<br>          &quot;{{ .Name }}&quot;,<br>          &quot;--cpus&quot;,<br>          &quot;{{ user `cpus` }}&quot;<br>        ],<br>        [<br>          &quot;modifyvm&quot;,<br>          &quot;{{ .Name }}&quot;,<br>          &quot;--nic1&quot;,<br>          &quot;nat&quot;,<br>          &quot;--nictype1&quot;,<br>          &quot;virtio&quot;<br>        ],<br>        [<br>          &quot;modifyvm&quot;,<br>          &quot;{{ .Name }}&quot;,<br>          &quot;--nic2&quot;,<br>          &quot;hostonly&quot;,<br>          &quot;--nictype2&quot;,<br>          &quot;virtio&quot;,<br>          &quot;--hostonlyadapter2&quot;,<br>          &quot;{{ user `host_adapter` }}&quot;<br>        ]<br>      ],<br>      &quot;vm_name&quot;: &quot;packer-{{ build_name }}&quot;<br>    },<br>    {<br>      &quot;name&quot;: &quot;qemu-base-debian&quot;,<br>      &quot;type&quot;: &quot;qemu&quot;,<br>      &quot;accelerator&quot;: &quot;kvm&quot;,<br>      &quot;boot_command&quot;: [<br>        &quot;&lt;esc&gt;&lt;wait&gt;&quot;,<br>        &quot;install &lt;wait&gt;&quot;,<br>        &quot;preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/qemu-preseed.cfg &lt;wait&gt;&quot;,<br>        &quot;DEBCONF_DEBUG=5 &lt;wait&gt;&quot;,<br>        &quot;debian-installer=en_US &lt;wait&gt;&quot;,<br>        &quot;auto=true &lt;wait&gt;&quot;,<br>        &quot;priority=high &lt;wait&gt;&quot;,<br>        &quot;locale=en_US &lt;wait&gt;&quot;,<br>        &quot;kbd-chooser/method=us &lt;wait&gt;&quot;,<br>        &quot;keyboard-configuration/xkb-keymap=us &lt;wait&gt;&quot;,<br>        &quot;netcfg/get_hostname=debian &lt;wait&gt;&quot;,<br>        &quot;netcfg/get_domain=  &lt;wait&gt;&quot;,<br>        &quot;netcfg/choose_interface=auto  &lt;wait&gt;&quot;,<br>        &quot;fb=false &lt;wait&gt;&quot;,<br>        &quot;debconf/frontend=noninteractive &lt;wait&gt;&quot;,<br>        &quot;console-setup/ask_detect=false &lt;wait&gt;&quot;,<br>        &quot;console-keymaps-at/keymap=us &lt;wait&gt;&quot;,<br>        &quot;&lt;enter&gt;&lt;wait&gt;&quot;<br>      ],<br>      &quot;boot_wait&quot;: &quot;{{ user `boot_wait` }}&quot;,<br>      &quot;disk_cache&quot;: &quot;writeback&quot;,<br>      &quot;disk_compression&quot;: <em>true</em>,<br>      &quot;disk_interface&quot;: &quot;virtio&quot;,<br>      &quot;disk_size&quot;: &quot;{{ user `disk_size` }}&quot;,<br>      &quot;format&quot;: &quot;qcow2&quot;,<br>      &quot;headless&quot;: &quot;{{ user `headless` }}&quot;,<br>      &quot;http_directory&quot;: &quot;http&quot;,<br>      &quot;iso_checksum_type&quot;: &quot;sha512&quot;,<br>      &quot;iso_checksum_url&quot;: &quot;{{ user `iso_checksum_url` }}&quot;,<br>      &quot;iso_urls&quot;: [<br>        &quot;iso/debian-{{ user `debian_version` }}-amd64-netinst.iso&quot;,<br>        &quot;{{ user `mirror` }}/{{ user `debian_version` }}/amd64/iso-cd/debian-{{ user `debian_version` }}-amd64-netinst.iso&quot;<br>      ],<br>      &quot;machine_type&quot;: &quot;pc&quot;,<br>      &quot;net_device&quot;: &quot;virtio-net-pci&quot;,<br>      &quot;output_directory&quot;: &quot;output-{{ build_name }}&quot;,<br>      &quot;shutdown_command&quot;: &quot;systemctl poweroff&quot;,<br>      &quot;ssh_password&quot;: &quot;packer123&quot;,<br>      &quot;ssh_timeout&quot;: &quot;{{ user `ssh_timeout` }}&quot;,<br>      &quot;ssh_username&quot;: &quot;root&quot;,<br>      &quot;vnc_bind_address&quot;: &quot;{{ user `rdp_bind_address` }}&quot;,<br>      &quot;qemuargs&quot;: [<br>        [<br>          &quot;-m&quot;,<br>          &quot;{{ user `memory` }}&quot;<br>        ],<br>        [<br>          &quot;-smp&quot;,<br>          &quot;{{ user `cpus` }}&quot;<br>        ]<br>      ],<br>      &quot;vm_name&quot;: &quot;packer-{{ build_name }}&quot;<br>    }<br>  ]<br>}</pre><p>We use Debian 9.6 now, but we started with 9.5.</p><p>To be able to change version, mirror and checksum URL we expose the following user variables (ref: <a href="https://www.packer.io/docs/templates/user-variables.html">https://www.packer.io/docs/templates/user-variables.html</a>).</p><pre>&quot;variables&quot;: {<br>  &quot;debian_version&quot;: &quot;9.6.0&quot;,<br>  &quot;mirror&quot;: &quot;http://mirror.host.ag/debian/debian-cd/&quot;,<br>  &quot;iso_checksum_url&quot;: &quot;http://mirror.host.ag/debian/debian-cd/9.6.0/amd64/iso-cd/SHA512SUMS&quot;<br>},</pre><p>And use them like this in the builders</p><pre>&quot;iso_checksum_type&quot;: &quot;sha512&quot;,<br>&quot;iso_checksum_url&quot;: &quot;{{ user `iso_checksum_url` }}&quot;,<br>&quot;iso_urls&quot;: [<br>  &quot;iso/debian-{{ user `debian_version` }}-amd64-netinst.iso&quot;,<br>  &quot;{{ user `mirror` }}/{{ user `debian_version` }}/amd64/iso-cd/debian-{{ user `debian_version` }}-amd64-netinst.iso&quot;<br>],</pre><p><em>It’s important to note that the first path in </em><em>iso_urls allows us to use a local iso and skip the download. Useful if run in network bandwidth constrained environment.</em></p><p>We run the following shell provisioners (more about them at <a href="https://www.packer.io/docs/provisioners/shell.html">https://www.packer.io/docs/provisioners/shell.html</a>)</p><ul><li>virtualbox guest additions, if it’s a virtualbox image</li><li>QEMU guest agent, if it’s a virtualbox image</li><li>SSH authorized_keys configuration</li><li>disabling of LVM metad service</li><li>cleanup</li></ul><p>The above provisioners are the ones that we must test.</p><p>Let’s first try running the packer build without tests in mind.</p><pre>&gt; packer build -var &#39;headless=true&#39; \<br>-var &#39;host_adapter=vboxnet0&#39; \<br>-only=virtualbox-base-debian \<br>-force packer-base-debian.json</pre><p>The above will not run the test suite. It’ll provision from a Debian 9.6 ISO, unattended/automated installation of Debian with debian-installer preseed configuration.</p><p>We expect the following output</p><pre>virtualbox-base-debian output will be in this color.</pre><pre>==&gt; virtualbox-base-debian: Cannot find &quot;Default Guest Additions ISO&quot; in vboxmanage output (or it is empty)<br>==&gt; virtualbox-base-debian: Retrieving Guest additions checksums<br>1 items:  4.47 KiB / 4.47 KiB [==============================================================================================================================================================================================================================================] 0s<br>    virtualbox-base-debian: Transferred: <a href="https://download.virtualbox.org/virtualbox/5.2.10/SHA256SUMS">https://download.virtualbox.org/virtualbox/5.2.10/SHA256SUMS</a><br>==&gt; virtualbox-base-debian: Retrieving Guest additions<br>    virtualbox-base-debian: Found already downloaded, initial checksum matched, no download needed: <a href="https://download.virtualbox.org/virtualbox/5.2.10/VBoxGuestAdditions_5.2.10.iso">https://download.virtualbox.org/virtualbox/5.2.10/VBoxGuestAdditions_5.2.10.iso</a><br>==&gt; virtualbox-base-debian: Retrieving ISO<br>    virtualbox-base-debian: Found already downloaded, initial checksum matched, no download needed: <a href="http://mirror.host.ag/debian/debian-cd//9.6.0/amd64/iso-cd/debian-9.6.0-amd64-netinst.iso">http://mirror.host.ag/debian/debian-cd//9.6.0/amd64/iso-cd/debian-9.6.0-amd64-netinst.iso</a><br>==&gt; virtualbox-base-debian: Starting HTTP server on port 8361<br>==&gt; virtualbox-base-debian: Creating virtual machine...<br>==&gt; virtualbox-base-debian: Creating hard drive...<br>==&gt; virtualbox-base-debian: Creating forwarded port mapping for communicator (SSH, WinRM, etc) (host port 3486)<br>==&gt; virtualbox-base-debian: Executing custom VBoxManage commands...<br>    virtualbox-base-debian: Executing: modifyvm packer-virtualbox-base-debian --memory 512<br>    virtualbox-base-debian: Executing: modifyvm packer-virtualbox-base-debian --cpus 1<br>    virtualbox-base-debian: Executing: modifyvm packer-virtualbox-base-debian --nic1 nat --nictype1 virtio<br>    virtualbox-base-debian: Executing: modifyvm packer-virtualbox-base-debian --nic2 hostonly --nictype2 virtio --hostonlyadapter2 vboxnet0<br>==&gt; virtualbox-base-debian: Starting the virtual machine...<br>    virtualbox-base-debian: The VM will be run headless, without a GUI. If you want to<br>    virtualbox-base-debian: view the screen of the VM, connect via VRDP without a password to<br>    virtualbox-base-debian: rdp://127.0.0.1:5958<br>==&gt; virtualbox-base-debian: Waiting 5s for boot...<br>==&gt; virtualbox-base-debian: Typing the boot command...<br>==&gt; virtualbox-base-debian: Connected to SSH!                                                                           <br>==&gt; virtualbox-base-debian: Uploading VirtualBox version info (5.2.10)                                              <br>==&gt; virtualbox-base-debian: Uploading VirtualBox guest additions ISO...                                                  <br>==&gt; virtualbox-base-debian: Provisioning with shell script: ./files/base-debian/00_guest_additions.sh</pre><pre>&lt;omitted output for readability&gt;<br>==&gt; virtualbox-base-debian: Provisioning with shell script: ./files/base-debian/01_ssh.sh<br>&lt;omitted output for readability&gt;<br>==&gt; virtualbox-base-debian: Provisioning with shell script: ./files/base-debian/02_disable_lvmetad.sh <br>==&gt; virtualbox-base-debian: Provisioning with shell script: ./files/base-debian/03_logging.sh<br>&lt;omitted output for readability&gt;<br>==&gt; virtualbox-base-debian: Provisioning with shell script: ./files/cleanup.sh<br>&lt;omitted output for readability&gt;<br>==&gt; virtualbox-base-debian: Running local shell script: ./serverspec/sleep_before_tests_packer.sh<br>==&gt; virtualbox-base-debian: Running local shell script: /tmp/packer-&lt;omitted output for readability&gt;<br>==&gt; virtualbox-base-debian: Gracefully halting virtual machine...<br>==&gt; virtualbox-base-debian: Preparing to export machine...<br>    virtualbox-base-debian: Deleting forwarded port mapping for the communicator (SSH, WinRM, etc) (host port 3486)<br>==&gt; virtualbox-base-debian: Exporting virtual machine...<br>    virtualbox-base-debian: Executing: export packer-virtualbox-base-debian --output output-virtualbox-base-debian/packer-virtualbox-base-debian.ova<br>==&gt; virtualbox-base-debian: Deregistering and deleting VM...<br>Build &#39;virtualbox-base-debian&#39; finished.</pre><pre>==&gt; Builds finished. The artifacts of successful builds are:<br>--&gt; virtualbox-base-debian: VM files in directory: output-virtualbox-base-debian</pre><p>If you see very similar to the above output — it’s working and we can move on to the actual test suite.</p><h3>How to run the test suite</h3><p>There were really only two choices:</p><ul><li>Run the test suite on the guest</li><li>Run the test suite on the host</li></ul><h3>“Run the test suite on the guest”:</h3><ul><li>(+) No need to have runtime and dependencies on the host</li><li>(=) Have “bootstrap” script for runtime and dependencies to be provisioned and executed to the guest</li><li>(-) Have “cleanup” script to remove all runtime and dependencies and also the test suite output such as coverage reports</li><li>(-) More complex for scenarios where you want to extract test artifacts such as coverage reports</li><li>(-) Almost surely double-work in terms of cleanup</li></ul><h3>“Run the test suite on the host”:</h3><ul><li>(-) Need to have runtime and dependencies on the host</li><li>(+) No need to have “bootstrap” scripts for runtime and dependencies to be provisioned, executed and cleaned up from the guest</li><li>(+) Test artifacts such as coverage reports will be already present on the host and easier to feed to other systems</li><li>(+) No double-work and complexity of cleanup</li></ul><h3>Executing the strategy of choice — “Run the test suite on the host”:</h3><p>We’re going to run the test suite via a shell-local provisioner (read more at <a href="https://www.packer.io/docs/provisioners/shell-local.html">https://www.packer.io/docs/provisioners/shell-local.html</a>).</p><p>Since shell-local will run it from our host machine to the guest we need to solve two problems:</p><ol><li>what credentials we can use to execute shell commands on the guest to verify behaviour</li><li>what’s the SSH IP and port we can use</li></ol><p>The “<strong>1.”</strong> is solvable easily since we’re the ones creating and provisioning the image. For simplicity sake, we’ll use the root user — root and password packer123 .</p><p>About “<strong>2.”</strong>, though</p><p>If you’ve been using HashiCorp Packer for a while, you probably have read the chain of threads:</p><ul><li><a href="https://github.com/hashicorp/packer/issues/3331">https://github.com/hashicorp/packer/issues/3331</a></li><li><a href="https://github.com/hashicorp/packer/issues/3889">https://github.com/hashicorp/packer/issues/3889</a></li><li><a href="https://github.com/hashicorp/packer/issues/4701">https://github.com/hashicorp/packer/issues/4701</a> and more.</li></ul><p>The verdict of that chain of threads — you can’t get your SSH communicator’s IP and port in the template.</p><p><strong>Getting the IP and port of the guest created by Packer options</strong></p><p>As the last choice regarding the testing strategy, this is also a binary choice.</p><p><strong>Choice 1. Get the guest IP from the guest machine’s network interface</strong></p><p>This can be done by executing a script with shell provisioner and retrieving the IP, saving the IP at for example /tmp/myip , downloading the file from the guest to the host using the file provisioner in download direction (read more at <a href="https://www.packer.io/docs/provisioners/file.html">https://www.packer.io/docs/provisioners/file.html</a>).</p><p>However, the above choice has a huge flaw — by default created guests have NAT-ed networking. You won’t be able to access them with their 10.x.x.x/8 CIDR IPs without extra hassle to port-forward SSH port 22.</p><p>Also if you don’t want to do the above, you can get away with using hostonly network adapters (as per VirtualBox terminology), but then you lose the ability to test NAT-only images.</p><p>Now let’s get to choice 2, which is the one we’ve picked.</p><p><strong>Choice 2. Re-use Packer’s SSH port forwarding used for the SSH communicator</strong></p><p>If we enable Packer to log with PACKER_LOG=1 and PACKER_LOG_PATH=packer.log we can parse the log file and the “verbose” output to get the following line</p><pre>==&gt; virtualbox-base-debian: Creating forwarded port mapping for communicator (SSH, WinRM, etc) (host port 3486)</pre><p>We can know how to get the port now. We can parse it since the port will be changing always. We can use a static port, but this is sometimes too naive luxury in dynamic environments such as CI.</p><p>The SSH address is known from a log message like:</p><pre>==&gt; virtualbox-base-debian: Using ssh communicator to connect: 127.0.0.1</pre><p>We won’t be parsing it since, so far based on Packer experience, it’s always going to be localhost.</p><p>This results in the following “glue” script used to make our test suite run against the guest (ref: <a href="https://github.com/syndbg/sumup-blog-hashicorp-packer-test/blob/master/serverspec/run_packer.sh">https://github.com/syndbg/sumup-blog-hashicorp-packer-test/blob/master/serverspec/run_packer.sh</a>)</p><pre>#!/usr/bin/env bash<br># NOTE: This file is run by packer provisioner `shell-local`.<br># Tests on developer machines are run inside `serverspec` dir using `bundle exec rspec`.</pre><pre># Arguments:<br># $1 = file path to the image-specific spec to run inside serverspec dir<br>set -ex</pre><pre>if [[ &quot;$MUST_RUN_TESTS&quot; != &quot;true&quot; ]]; then<br>    exit 0<br>fi</pre><pre>LOG=&quot;packer.log&quot;</pre><pre>ip_and_port=&quot;$(grep -F &#39;SSH handshake err: ssh: handshake failed: read tcp&#39; &quot;$LOG&quot; \<br>    | pcregrep -o1 &#39;\-&gt;([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]{1,5})&#39; \<br>    | sort \<br>    | uniq \<br>    | head)&quot;</pre><pre>if [[ &quot;$ip_and_port&quot; == &quot;&quot; ]]; then<br>    port=&quot;$(grep -F &#39;Found port for communicator (SSH, WinRM, etc): &#39; &quot;$LOG&quot; \<br>        | pcregrep -o1 &#39;Found\s+port\s+for\s+communicator\s+.+:\s+([0-9]{1,5})&#39; \<br>        | sort \<br>        | uniq \<br>        | head)&quot;</pre><pre>if [[ &quot;$port&quot; == &quot;&quot; ]]; then<br>        port=&quot;$(grep -F &#39;Creating forwarded port mapping for communicator (SSH, WinRM, etc) &#39; &quot;$LOG&quot; \<br>            | pcregrep -o1 &#39;Creating\s+forwarded\s+port\s+mapping.+\s+([0-9]{1,5})&#39; \<br>            | sort \<br>            | uniq \<br>            | head)&quot;<br>    fi<br>    # NOTE: Packer uses localhost almost always.<br>    # parse host from log message `Using ssh communicator to connect: ` only if needed.<br>    ip_and_port=&quot;127.0.0.1:$port&quot;<br>fi</pre><pre>echo &quot;Found IP and port in $LOG: $ip_and_port&quot;</pre><pre>export TARGET_HOST=$(echo $ip_and_port | cut -d &quot;:&quot; -f1)<br>export TARGET_PORT=$(echo $ip_and_port | cut -d &quot;:&quot; -f2)</pre><pre>echo &quot;Going to use host: $TARGET_HOST&quot;<br>echo &quot;Going to use port: $TARGET_PORT&quot;<br>echo &quot;Going to use user: $TARGET_USER&quot;</pre><pre>cd ./serverspec &amp;&amp; bundle exec rspec $1</pre><p>(more cases are handled since QEMU and Virtualbox builders don’t have consistent log messages in between them)</p><p>We integrate the above run_packer.sh glue code in the Packer debian template using the following provisioner</p><pre>{                                         <br>      &quot;type&quot;: &quot;shell-local&quot;,                    <br>      &quot;command&quot;: &quot;./serverspec/run_packer.sh ./spec/base_debian_spec.rb&quot;,<br>      &quot;environment_vars&quot;: [                               <br>        &quot;MUST_RUN_TESTS={{ user `must_run_tests` }}&quot;,<br>        &quot;SUDO_PASSWORD=packer123&quot;,                    <br>        &quot;TARGET_PASSWORD=packer123&quot;,                                <br>        &quot;TARGET_USER=root&quot;,          <br>        &quot;BUILDER_TYPE={{ build_type }}&quot;        <br>      ]                        <br>    }</pre><p>We specify whether to skip or run the test via Packer user variable must_run_tests specified like packer build -var must_run_tests=true|false and we explicitly give the BUILDER_TYPE via Packer template builtin variable build_type (read more about built-in template variables <a href="https://www.packer.io/docs/templates/engine.html">https://www.packer.io/docs/templates/engine.html</a>).</p><p>The BUILDER_TYPE is going to be useful for us to test positively/negatively whether certain packages are present or missing if we use QEMU or VirtualBox based images.</p><h3>The test suite structure:</h3><pre>serverspec<br>├── Gemfile<br>├── Gemfile.lock<br>├── run_packer.sh (the glue code)<br>├── sleep_before_tests_packer.sh (will be covered later)<br>└── spec<br>    ├── base_debian_spec.rb<br>    ├── goldenfiles<br>    │   ├── logging<br>    │   │   └── journald.conf<br>    │   └── ssh<br>    │       ├── authorized_keys<br>    │       ├── root_rsa.pub<br>    │       └── users_rsa.pub<br>    ├── spec_helper.rb<br>    └── support<br>        └── shared_examples<br>            ├── disable_lvmetad.rb<br>            ├── logging.rb<br>            ├── qemu_guest_agent.rb<br>            ├── ssh.rb<br>            └── virtualbox_guest_additions.rb</pre><p>We follow proven best practices from http://betterspecs.org/.</p><p>We use bundler to manage our dependencies.</p><p>RSpec + ServerSpec (resources and command runner) are used to verify state.</p><p>We also use test if provisioned files match using golden files (<a href="https://softwareengineering.stackexchange.com/a/358792">https://softwareengineering.stackexchange.com/a/358792</a>) via the <a href="https://rubygems.org/gems/rspec-golden-files">https://rubygems.org/gems/rspec-golden-files</a> gem.</p><p><strong>Let’s look at our </strong><strong>spec_helper.rb</strong></p><pre># frozen_string_literal: true</pre><pre>require &#39;serverspec&#39;<br>require &#39;net/ssh&#39;<br>require &#39;rspec-golden-files&#39;</pre><pre>Dir[<br>    File.expand_path(<br>      File.join(<br>        File.dirname(__FILE__),<br>        &#39;support&#39;, &#39;**&#39;, &#39;*.rb&#39;<br>      )<br>    )<br>].each { |f| require f }</pre><pre>set :backend, :ssh<br>set :sudo_password, ENV[&#39;SUDO_PASSWORD&#39;]<br>set :paranoid, false</pre><pre>options = Net::SSH::Config.for(ENV[&#39;TARGET_HOST&#39;])</pre><pre>options[:user] = ENV[&#39;TARGET_USER&#39;] if ENV[&#39;TARGET_USER&#39;]<br>options[:user] ||= Etc.getlogin<br>options[:port] = ENV[&#39;TARGET_PORT&#39;] if ENV[&#39;TARGET_PORT&#39;]<br>options[:password] = ENV[&#39;TARGET_PASSWORD&#39;] if ENV[&#39;TARGET_PASSWORD&#39;]<br>options[:auth_methods] = %w[password publickey]</pre><pre>set :host,        options[:host_name] || ENV[&#39;TARGET_HOST&#39;]<br>set :ssh_options, options</pre><pre># NOTE: Disable sudo since it&#39;s not used.<br># Tests are assumed to be run as root.<br>set :disable_sudo, true</pre><pre>if !ENV[&#39;BUILDER_TYPE&#39;] || ENV[&#39;BUILDER_TYPE&#39;].empty?<br>  raise &#39;no environment variable `BUILDER_TYPE` provided&#39;<br>end</pre><pre>is_virtualbox = ENV[&#39;BUILDER_TYPE&#39;]&amp;.start_with?(&#39;virtualbox&#39;)<br>is_qemu = ENV[&#39;BUILDER_TYPE&#39;] == &#39;qemu&#39;</pre><pre>RSpec.configure do |config|<br>  # NOTE: Disable `should` syntax.<br>  # Expect is the only absolute.<br>  # ref: <a href="http://www.betterspecs.org/#should">http://www.betterspecs.org/#should</a><br>  config.expect_with :rspec do |expectations|<br>    expectations.syntax = :expect<br>  end</pre><pre>config.include RSpec::GoldenFiles<br>  config.filter_run_excluding is_virtualbox: !is_virtualbox<br>  config.filter_run_excluding is_not_virtualbox: is_virtualbox<br>  config.filter_run_excluding is_qemu: !is_qemu<br>  config.filter_run_excluding is_not_qemu: is_qemu<br>end</pre><pre># Set environment variables<br># set :env, :LANG =&gt; &#39;C&#39;, :LC_MESSAGES =&gt; &#39;C&#39;</pre><pre># NOTE: Set PATH since</pre><p>We provide accurate SSH communication options via</p><pre>options = Net::SSH::Config.for(ENV[&#39;TARGET_HOST&#39;])</pre><pre>options[:user] = ENV[&#39;TARGET_USER&#39;] if ENV[&#39;TARGET_USER&#39;]<br>options[:user] ||= Etc.getlogin<br>options[:port] = ENV[&#39;TARGET_PORT&#39;] if ENV[&#39;TARGET_PORT&#39;]<br>options[:password] = ENV[&#39;TARGET_PASSWORD&#39;] if ENV[&#39;TARGET_PASSWORD&#39;]<br>options[:auth_methods] = %w[password publickey]</pre><pre>set :host,        options[:host_name] || ENV[&#39;TARGET_HOST&#39;]<br>set :ssh_options, options</pre><p>We exclude tests not applicable for non-QEMU or non-VirtualBox via RSpec exclusion filters (read more at <a href="https://relishapp.com/rspec/rspec-core/docs/filtering/exclusion-filters">https://relishapp.com/rspec/rspec-core/docs/filtering/exclusion-filters</a>)</p><pre>is_virtualbox = ENV[&#39;BUILDER_TYPE&#39;]&amp;.start_with?(&#39;virtualbox&#39;)<br>is_qemu = ENV[&#39;BUILDER_TYPE&#39;] == &#39;qemu&#39;</pre><pre>config.include RSpec::GoldenFiles<br>  config.filter_run_excluding is_virtualbox: !is_virtualbox<br>  config.filter_run_excluding is_not_virtualbox: is_virtualbox<br>  config.filter_run_excluding is_qemu: !is_qemu<br>  config.filter_run_excluding is_not_qemu: is_qemu<br>end</pre><h3>Base-debian test file and how we mirror provision scripts with “mixin” tests</h3><p>Our test is simply</p><pre># frozen_string_literal: true</pre><pre>require &#39;spec_helper&#39;</pre><pre>describe &#39;base debian image&#39; do<br>  include_examples &#39;virtualbox guest additions&#39;<br>  include_examples &#39;qemu guest agent&#39;<br>  include_examples &#39;SSH&#39;<br>  include_examples &#39;disable lvmetad&#39;<br>  include_examples &#39;logging&#39;<br>end</pre><p>This structure of extracting provision script test logic to shareable examples (read more at <a href="https://relishapp.com/rspec/rspec-core/docs/example-groups/shared-examples">https://relishapp.com/rspec/rspec-core/docs/example-groups/shared-examples</a>) allows us to change as fast and easy as you can remove provisioners from your Packer template.</p><p>Let’s look at our first included example “virtualbox guest additions” .</p><p>It needs to test the following provision script:</p><pre>#!/usr/bin/env bash<br>echo &quot;Installing VirtualBox guest additions&quot;</pre><pre>apt install -y linux-headers-$(uname -r) build-essential<br>apt install -y dkms</pre><pre>VBOX_VERSION=$(cat /root/.vbox_version)<br>mount -o loop /root/VBoxGuestAdditions_$VBOX_VERSION.iso /mnt<br>sh /mnt/VBoxLinuxAdditions.run<br>umount /mnt<br>rm /root/VBoxGuestAdditions_$VBOX_VERSION.iso</pre><p>The test file looks like (relevant ServerSpec resource docs at <a href="https://serverspec.org/resource_types.html">https://serverspec.org/resource_types.html</a>)</p><pre># frozen_string_literal: true</pre><pre>require &#39;spec_helper&#39;</pre><pre>RSpec.shared_examples &#39;virtualbox guest additions&#39; do<br>  context &#39;when running in virtualbox guest&#39;, is_virtualbox: true do<br>    context &#39;with dependencies&#39; do<br>      describe package(&#39;build-essential&#39;) do<br>        it { is_expected.to be_installed }<br>      end</pre><pre>describe package(&#39;dkms&#39;) do<br>        it { is_expected.to be_installed }<br>      end</pre><pre>describe &#39;package &quot;linux-headers&quot;&#39; do<br>        it do<br>          kernel_version = command(&#39;uname -r&#39;).stdout.strip<br>          pkg = package(&quot;linux-headers-#{kernel_version}&quot;)<br>          expect(pkg).to be_installed<br>        end<br>      end<br>    end</pre><pre>describe file(&#39;/root/.vbox_version&#39;) do<br>      it { is_expected.to exist }<br>    end</pre><pre>describe kernel_module(&#39;vboxsf&#39;) do<br>      it { is_expected.to be_loaded }<br>    end<br>  end</pre><pre>context &#39;when not running in virtualbox guest&#39;, is_not_virtualbox: true do<br>    describe kernel_module(&#39;vboxsf&#39;) do<br>      it { is_expected.not_to be_loaded }<br>    end<br>  end<br>end</pre><p>It’s important to note the is_not_virtualbox: true and is_virtualbox: true “tags” that are used for exclusion if it’s QEMU — we’re not going to test the is_virtualbox case at all. We’re only going to test the is_not_virtualbox one. For VirtualBox — the opposite.</p><p>And let’s see an example with a golden file, verifying that included example SSH matches the desired behaviour and guest state.</p><p>Provision script: (read more about PACKER_HTTP_ADDR at <a href="https://www.packer.io/docs/provisioners/shell.html#packer_http_addr">https://www.packer.io/docs/provisioners/shell.html#packer_http_addr</a>)</p><pre>#!/usr/bin/env bash<br>echo &quot;Configure SSH keys&quot;<br>apt update<br>apt install -y wget</pre><pre>wget <a href="http://${PACKER_HTTP_ADDR}/root_rsa.pub">http://${PACKER_HTTP_ADDR}/root_rsa.pub</a> -O /tmp/root_rsa.pub<br>cat /tmp/root_rsa.pub &gt;&gt; /root/.ssh/authorized_keys<br>wget <a href="http://${PACKER_HTTP_ADDR}/users_rsa.pub">http://${PACKER_HTTP_ADDR}/users_rsa.pub</a> -O /tmp/users_rsa.pub<br>cat /tmp/users_rsa.pub &gt;&gt; /root/.ssh/authorized_keys</pre><p>root_rsa.pub</p><pre>ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCmauj4TaqalXVBnrMPSCaWx47EWqw7VE7H4fxp78YqUaNzhz1Y7f+BVmGCVCzvLMWF5PK+Yx66QuOu/R1xVRoyZRMn19QR/Roy7SKKbPT5GR//MP9NM9X78safSs318CRPsKKkkz5N2Qjv0nl5KzUtg6X3oKhgEptRIMnMu5y6lj2MxVxtKPVDLBm/vFiR6AzpN8/0/gD/mOgYpwbj0eOUZUy4I+HCovTFqQDhredvaS2h5ugyRL9EsoFSVVbgkdtNpk1HKjNf1bI0dEELgCxGlgG2cdnQXOYrtFX+Q3+0Y/bv3ruO43GsI88QuByHNR4okUnEkb0svQhQg2X8Z2fP <a href="mailto:root@example.com">root@example.com</a></pre><p>users_rsa.pub</p><pre>ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJJO1eEqDdRf2sJRt6cuaKxDYxfO4rU4EL4Q29zaCuc4l/YVzGKasmq43griT5QbmYS4K+xgVxwAgfgBZ7CsqQuv85KoJqjZFAjehptEUUtiIeZzvFTKd2nsfGzoR+UGzI5hjyJIglzilpqpUL2FseQzHFBne66kokzv1vAMe6kBMOaqflw1CaurAP6R64sAsIwaLRjmaCzpoWyFCjy4m4Lj5LV41GGHusDt3ASJnZwwTWPKxPH6BMlWgCWmHH6y8sqVxLyOaJYnXXJE3hdnxdbJ2UWgigJEjHakFU2HGPF0AWJV585xG1fY89U635fsMZ2lFThKIDN367KUTPz6Sb <a href="mailto:users@example.com">users@example.com</a></pre><p>Then our test file looks like</p><pre># frozen_string_literal: true</pre><pre>require &#39;spec_helper&#39;</pre><pre>RSpec.shared_examples &#39;SSH&#39; do<br>  describe package(&#39;wget&#39;) do<br>    it { is_expected.to be_installed }<br>  end</pre><pre>describe &#39;contents of authorized_keys&#39; do<br>    it do<br>      guest_file = file(&#39;/root/.ssh/authorized_keys&#39;).content</pre><pre>expect(guest_file).to match_golden_file(&#39;spec/goldenfiles/ssh/authorized_keys&#39;)<br>    end<br>  end<br>end</pre><p>We use match_golden_file to match the following golden file</p><pre>ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCmauj4TaqalXVBnrMPSCaWx47EWqw7VE7H4fxp78YqUaNzhz1Y7f+BVmGCVCzvLMWF5PK+Yx66QuOu/R1xVRoyZRMn19QR/Roy7SKKbPT5GR//MP9NM9X78safSs318CRPsKKkkz5N2Qjv0nl5KzUtg6X3oKhgEptRIMnMu5y6lj2MxVxtKPVDLBm/vFiR6AzpN8/0/gD/mOgYpwbj0eOUZUy4I+HCovTFqQDhredvaS2h5ugyRL9EsoFSVVbgkdtNpk1HKjNf1bI0dEELgCxGlgG2cdnQXOYrtFX+Q3+0Y/bv3ruO43GsI88QuByHNR4okUnEkb0svQhQg2X8Z2fP <a href="mailto:root@example.com">root@example.com</a><br>ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJJO1eEqDdRf2sJRt6cuaKxDYxfO4rU4EL4Q29zaCuc4l/YVzGKasmq43griT5QbmYS4K+xgVxwAgfgBZ7CsqQuv85KoJqjZFAjehptEUUtiIeZzvFTKd2nsfGzoR+UGzI5hjyJIglzilpqpUL2FseQzHFBne66kokzv1vAMe6kBMOaqflw1CaurAP6R64sAsIwaLRjmaCzpoWyFCjy4m4Lj5LV41GGHusDt3ASJnZwwTWPKxPH6BMlWgCWmHH6y8sqVxLyOaJYnXXJE3hdnxdbJ2UWgigJEjHakFU2HGPF0AWJV585xG1fY89U635fsMZ2lFThKIDN367KUTPz6Sb <a href="mailto:users@example.com">users@example.com</a></pre><p>We’re going to skip reviewing the other test suite files, cause they won’t be that much different.</p><h3>Executing the test suite</h3><p>Since we chose to get the SSH IP and port from the packer log file, we must always run our tests with PACKER_LOG=1 and PACKER_LOG_PATH=packer.log (and `packer.log` must be in the same dir as the packer template).</p><p>Run the following command to create guest image with tests before exporting it:</p><pre>PACKER_LOG=1 PACKER_LOG_PATH=./packer.log packer build \<br>-var &#39;must_run_tests=true&#39; \<br>-var &#39;headless=true&#39; \<br>-var &#39;host_adapter=vboxnet0&#39; \<br>-only=virtualbox-base-debian \<br>-force packer-base-debian.json</pre><p>We must see output very similar to the one below with a successful outcome</p><pre>virtualbox-base-debian: base debian image<br>    virtualbox-base-debian:   when running in virtualbox guest<br>    virtualbox-base-debian:     with dependencies<br>    virtualbox-base-debian:       Package &quot;build-essential&quot;<br>    virtualbox-base-debian:         should be installed<br>    virtualbox-base-debian:       Package &quot;dkms&quot;<br>    virtualbox-base-debian:         should be installed<br>    virtualbox-base-debian:       package &quot;linux-headers&quot;<br>    virtualbox-base-debian:         should be installed<br>    virtualbox-base-debian:     File &quot;/root/.vbox_version&quot;<br>    virtualbox-base-debian:       should exist<br>    virtualbox-base-debian:     Kernel module &quot;vboxsf&quot;<br>    virtualbox-base-debian:       should be loaded<br>    virtualbox-base-debian:   when not running in qemu guest<br>    virtualbox-base-debian:     Package &quot;qemu-guest-agent&quot;<br>    virtualbox-base-debian:       should not be installed<br>    virtualbox-base-debian:   Package &quot;wget&quot;<br>    virtualbox-base-debian:     should be installed<br>    virtualbox-base-debian:   contents of authorized_keys<br>    virtualbox-base-debian:     should be matching golden file with name spec/goldenfiles/ssh/authorized_keys<br>    virtualbox-base-debian:   /etc/lvm/lvm.conf<br>    virtualbox-base-debian:     should contain &quot;use_lvmetad = 0&quot;<br>    virtualbox-base-debian:   File &quot;/var/log/journal&quot;<br>    virtualbox-base-debian:     should be exists<br>    virtualbox-base-debian:   File &quot;/etc/systemd/journald.conf&quot;<br>    virtualbox-base-debian:     should be matching golden file with name spec/goldenfiles/logging/journald.conf<br>    virtualbox-base-debian:   Service &quot;systemd-journald&quot;<br>    virtualbox-base-debian:     should be enabled<br>    virtualbox-base-debian:<br>    virtualbox-base-debian: Finished in 3.56 seconds (files took 0.18682 seconds to load)<br>    virtualbox-base-debian: 12 examples, 0 failures<br>    virtualbox-base-debian:<br>==&gt; virtualbox-base-debian: Gracefully halting virtual machine...<br>==&gt; virtualbox-base-debian: Preparing to export machine...<br>    virtualbox-base-debian: Deleting forwarded port mapping for the communicator (SSH, WinRM, etc) (host port 3462)<br>==&gt; virtualbox-base-debian: Exporting virtual machine...<br>    virtualbox-base-debian: Executing: export packer-virtualbox-base-debian --output output-virtualbox-base-debian/packer-virtualbox-base-debian.ova<br>==&gt; virtualbox-base-debian: Deregistering and deleting VM...<br>Build &#39;virtualbox-base-debian&#39; finished.</pre><pre>==&gt; Builds finished. The artifacts of successful builds are:<br>--&gt; virtualbox-base-debian: VM files in directory: output-virtualbox-base-debian</pre><h3>Debugging the test suite</h3><p>We’ve also added a provisioner that sleeps just before the test suite</p><pre>{<br>      &quot;type&quot;: &quot;shell-local&quot;,<br>      &quot;script&quot;: &quot;./serverspec/sleep_before_tests_packer.sh&quot;,<br>      &quot;environment_vars&quot;: [<br>        &quot;MUST_SLEEP_BEFORE_TESTS={{ user `must_sleep_before_tests` }}&quot;<br>      ]<br>    },</pre><p>It just does sleep 99999 so that you can develop and extend the test suite without the machine existing on you and Packer removing the SSH port forwarding.</p><p>The above provisioner can be run using</p><pre>PACKER_LOG=1 PACKER_LOG_PATH=./packer.log packer build \<br>-var &#39;must_sleep_before_tests=true&#39; \<br>-var &#39;headless=true&#39; \<br>-var &#39;host_adapter=vboxnet0&#39; \<br>-only=virtualbox-base-debian \<br>-force packer-base-debian.json</pre><h3>Where to go from here</h3><p>You have a good idea what we do in SumUp to test our images created via Packer.</p><p>We have a bit more complex image hierarchies and test suites, but they’re expanded upon this simple one.</p><p>You can use this one as a template.</p><p>Again, the source code is at <a href="https://github.com/syndbg/sumup-blog-hashicorp-packer-test">https://github.com/syndbg/sumup-blog-hashicorp-packer-test</a></p><p>Thank you for your time, Anton.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bb2bd065441" width="1" height="1" alt=""><hr><p><a href="https://medium.com/sumup-engineering/image-creation-and-testing-with-hashicorp-packer-and-serverspec-bb2bd065441">Image creation and testing with HashiCorp Packer and ServerSpec</a> was originally published in <a href="https://medium.com/sumup-engineering">SumUp Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Optimize your JS code using Google's V8 internals]]></title>
            <link>https://medium.com/sumup-engineering/optimize-your-js-code-using-googles-v8-internals-49955fd7d4a5?source=rss----bc8d70b3a259---4</link>
            <guid isPermaLink="false">https://medium.com/p/49955fd7d4a5</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[v8]]></category>
            <category><![CDATA[nodejs]]></category>
            <dc:creator><![CDATA[Rodrigo Souza]]></dc:creator>
            <pubDate>Mon, 17 Sep 2018 20:45:34 GMT</pubDate>
            <atom:updated>2018-09-17T20:45:34.513Z</atom:updated>
            <content:encoded><![CDATA[<p>At BrazilJS 2018 this year in Porto Alegre, <a href="https://github.com/mathiasbynens">Mathias Bynens</a> (a V8 developer at Google) shared some insights for code optimization if you are using (and you probably are) the V8 engine for Javascript. Here&#39;s what I learned.</p><p>For those who haven&#39;t heard of it, <strong>Google’s V8</strong> is an open source JavaScript engine written in <strong>C++</strong> developed by <strong>The Chromium Project</strong> for <strong>Chromium</strong> and <strong>Google Chrome</strong> web browser. It means that when you use Chrome and it detects JavaScript code, it passes that code to V8 to run the compilation and then your computer executes the result. It&#39;s also the cornerstone of the popular backend framework called <a href="https://nodejs.org/en/">Node.js</a>.</p><p>In this post, we’re going to see how V8 optimizes your code using a primitive called <strong>element kinds</strong>.</p><p>When you run JavaScript and assign variables in it, the V8 engine keeps track of the value types you&#39;re saving in memory besides the value itself. These <em>value types </em>are the element kinds we&#39;re talking about. Take this array as example:</p><p>const myArr = [1, 2, 3]</p><blockquote>At the language-level, it&#39;s all Numbers to JavaScript for it doesn&#39;t make distinction between <em>integers</em>, <em>floats </em>or <em>doubles</em>. But internally it does much more than that: V8 also identifies that it contains only <em>integers</em> and assign PACKED_SMI_ELEMENTS to myArr.</blockquote><p>Forget PACKED_ portion for now…</p><h3>Common Element Kinds</h3><p>SMI_ELEMENTS means it contains only <strong>SM</strong>all <strong>I</strong>ntegers and it can make optimizations when you&#39;re accessing it later. However, as soon as you add 3.14 to that array, it becomes PACKED_DOUBLES_ELEMENTS immediately and it can never go back to PACKED_SMI_ELEMENTS no matter what you do. If you go further and add &#39;x&#39; it is demoted to PACKED_ELEMENTS and becomes even more generic!</p><pre>const array = [1, 2, 3];<br>// elements kind: PACKED_SMI_ELEMENTS<br>array.push(3.14);<br>// elements kind: PACKED_DOUBLE_ELEMENTS<br>array.push(&#39;x&#39;);<br>// elements kind: PACKED_ELEMENTS</pre><p>That&#39;s the first thing you have to keep in mind:</p><h4>Rule 1: Always try to be as high as possible in the pyramid of <em>element kinds</em></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/541/1*Cp1eYJMl_eTSsZMYldWhHg.png" /><figcaption>Element kinds pyramid used to classify array of objects in JavaScript. The higher the objects are in pyramid the more optimized it is when doing operations on it afterwards. (got from <a href="https://mathiasbynens.be/">Mathias Bynens&#39;s slides</a>)</figcaption></figure><p>Before digging into how V8 makes these optimizations, let&#39;s unbox that PACKED_ portion we left aside.</p><h3>Holey vs Packed</h3><p>So far, our array values are [1, 2, 3, 3.14, &#39;x&#39;] and is considered by V8 engine as PACKED_ELEMENTS at this point. Now, consider this line of code:</p><p>myArr[9] = 1</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/739/1*qzgHNeTWglPZt1B5Cuifrw.png" /><figcaption>Our array has holes from index 5 to 8</figcaption></figure><p>We&#39;ve demoted myArr even more. It&#39;s now considered a HOLEY_ELEMENTS since it contains undefined values and it can never becomePACKED again. So we end up with this<strong> elements kinds lattice.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*UVqMOw7tO7_IqQNyiqiJQg.png" /><figcaption>Elements Kinds Lattice (Source: <a href="https://v8project.blogspot.com/2017/09/elements-kinds-in-v8.html">https://v8project.blogspot.com/2017/09/elements-kinds-in-v8.html</a>)</figcaption></figure><p>It&#39;s only possible to transition <em>downwards</em>. If you want to have PACKED_SMI_ELEMENTS again you must create a brand new<strong> </strong>array containing only integers with no holes in it. Hence…</p><h4>Rule 2: Always try to make your elements packed from the very beginning</h4><p>Take a look at this piece of code:</p><pre>const array = new Array(3); // HOLEY_SMI_ELEMENTS</pre><pre>array[0] = &#39;a&#39;; // HOLEY_ELEMENTS<br>array[1] = &#39;b&#39;; // HOLEY_ELEMENTS<br>array[2] = &#39;c&#39;; // HOLEY_ELEMENTS (still!)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/1*2Wbyqt0vezFqu2egvfRcOA.png" /><figcaption>Array values at the end of the code</figcaption></figure><p>Even though array is fulfilled, it&#39;s not possible to transition back to PACKED. It&#39;s too late and array is still a HOLEY_ELEMENTS kind.</p><h3>Optimizations done in V8</h3><p>Let&#39;s go for the fun part! And for this, I&#39;m going to propose something different, let&#39;s invert the roles now: imagine <strong>you</strong> are the developer behind V8 engine and you must make V8 respond properly to JS code (come on you can do it!). Let&#39;s recap:</p><blockquote>myArr is [1, 2, 3, 3.14, &#39;x&#39;, undefined, undefined, undefined, undefined, 1]. Indexes 5–8 are undefined because of the hole we created instead of having something assigned (we could assign undefined for example). Therefore there&#39;s nothing in its memory addresses but we know JavaScript must return undefined in this case.</blockquote><blockquote>I want to access myArr[8] which is a HOLEY_ELEMENTS. What you must do to ensure the return is undefined given you don&#39;t know much about myArr structure and you don&#39;t have this value properly saved in memory address?</blockquote><p>Don&#39;t be so harsh on yourself and take some time to think about it… Ok, time&#39;s up!</p><p>If you guessed correctly, Google&#39;s V8 engine makes the following checks to make sure the value in myArr[8] is undefined considering it&#39;s a HOLEY_ELEMENTS kind:</p><ol><li>8 &gt;= 0 &amp;&amp; 8 &lt; myArr.length (boundary checks) returns <strong>true</strong>. So it&#39;s allowed to continue…</li><li>hasOwnProperty(myArr, 8) returns <strong>false</strong>. Nope, it&#39;s not here, there&#39;s a couple of checks left to do…</li><li>hasOwnProperty(Array.prototype, 8) returns <strong>false. </strong>Not here also, let’s try somewhere else…</li><li>hasOwnProperty(Object.prototype, 8) returns <strong>false</strong>. Cannot find it anywhere. OK, let&#39;s return undefined then.</li><li>Return undefined.</li></ol><p>Seems like a bunch of duplicate checks, right? And, yes, it&#39;s kind of hard to guess straightaway. Now, let&#39;s take a look how it works for a different element kind. Consider the same example but for<strong> </strong>PACKED_ELEMENTS:</p><pre>const packedArr = [42, undefined, 42];<br>packedArr[1];</pre><p>V8 engine makes fewer checks:</p><ol><li>8 &gt;= 0 &amp;&amp; 8 &lt; packedIntArr.length returns <strong>true</strong>. Since it&#39;s PACKED, the value surely exists, you <strong>can</strong> return whatever you find in memory.</li><li>Return undefined.</li></ol><blockquote>Even though the return value is the same, V8 has done far less work behind the scenes. Therefore, you may want to keep your data as packed as possible as well.</blockquote><p>This performance also impacts on several other common operations:</p><ul><li>Array.prototype.forEach</li><li>Array.prototype.map</li><li>Array.prototype.filter</li><li>Array.prototype.some</li><li>Array.prototype.every</li><li>Array.prototype.reduce</li><li>Array.prototype.reduceRight</li><li>Array#{find, findIndex}</li></ul><blockquote>Since <strong>Chrome version 70 </strong>all the operations listed above are already optimized inside their element kind. This means that if you want better performance, keep your data structure similar to its kind as possible.</blockquote><p>Last but not least…</p><h4>Rule 3: When you&#39;re iterating through your collection, avoid out-of-bound reads</h4><p>Use whatever you prefer: array.forEach or item of array (both has the <strong>same</strong> performance nowadays) but avoid reaching out of bounds indexes like the code below:</p><p>for (let i = 0, item; (item = array[i]) != null; i++) { doSomething(); }</p><p>This pretty much concludes this type of optimization in the V8 engine. Of course, there are not only 6 elements kinds. There are <a href="https://cs.chromium.org/chromium/src/v8/src/elements-kind.h?l=14&amp;rcl=ec37390b2ba2b4051f46f153a8cc179ed4656f5d">21 different elements kinds</a> and each kind is optimized its own way, but I&#39;m sure you&#39;ll start to pay more attention on how you define your data structure after reading this post through.</p><h3>TL;DR</h3><p>In order to let V8 engine make better optimizations on your JavaScript code, you may want to follow some guidelines when writing it:</p><ul><li>Avoid element kind transitions</li><li>Avoid holes</li><li>Avoid out-of-bounds reads</li><li>Prefer arrays over array-like objects</li></ul><p>All in all, write modern, idiomatic JavaScript and let the JavaScript engine worry about making it fast. Thanks Mathias to for the awesome talk at BrazilJS and for sharing your knowledge!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=49955fd7d4a5" width="1" height="1" alt=""><hr><p><a href="https://medium.com/sumup-engineering/optimize-your-js-code-using-googles-v8-internals-49955fd7d4a5">Optimize your JS code using Google&#39;s V8 internals</a> was originally published in <a href="https://medium.com/sumup-engineering">SumUp Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>