<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Envoy Engineering - Medium]]></title>
        <description><![CDATA[How Envoy does engineering - Medium]]></description>
        <link>https://envoy.engineering?source=rss----d8aa51f79fb---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Envoy Engineering - Medium</title>
            <link>https://envoy.engineering?source=rss----d8aa51f79fb---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Tue, 14 Apr 2026 15:53:29 GMT</lastBuildDate>
        <atom:link href="https://envoy.engineering/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Hackweek 2018 Recap]]></title>
            <link>https://envoy.engineering/hackweek-2018-recap-bd47dd0441f8?source=rss----d8aa51f79fb---4</link>
            <guid isPermaLink="false">https://medium.com/p/bd47dd0441f8</guid>
            <category><![CDATA[product]]></category>
            <category><![CDATA[design]]></category>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[hackathons]]></category>
            <dc:creator><![CDATA[Bill Heaton]]></dc:creator>
            <pubDate>Thu, 04 Oct 2018 00:06:36 GMT</pubDate>
            <atom:updated>2018-10-04T00:06:36.016Z</atom:updated>
            <content:encoded><![CDATA[<p>A story from the Engineering, Product and Design crew at Envoy, Inc.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PvWHEaCyvf3YfT4dx44OHQ.jpeg" /><figcaption>Challenge the Status Quo</figcaption></figure><p>This summer ⛱ we paused our current development for one week to focus on innovation. This focus was an opportunity to invest in both our engineering, product, and design staff, as well as invest in future product ideas. We worked on new product ideas to extend our product’s use cases, applications for internal tools, new products to make our own workplace better, improving our current technology, and improving user experiences, e.g. accessibility.</p><p>Why stop forward development to experiment? Well, work should be fun, creativity should be common, a good idea 💡 can come from anyone. So, why not build something we believe will be great?</p><h4>Orchestrating a hackweek ⚒</h4><p>Our staff pitched their ideas/projects then organized by teaming up based on personal interest. We posted internal <em>help wanted</em> notices on a shared spreadsheet. Next we teamed up and joined project channels in our Slack chat. To create time to hack, we paused our current work in progress, likewise our scheduled ceremonies. This provided the freedom to hack on the ideas which our people believe will make a better future for our mission, to challenge the status quo of workplace technology. We focus on creating a better and safer workplace; and deliver solutions for visitor and employee experiences in the office that stand out. So our aim was to challenge ourselves to build and demo as many prototypes of <em>something awesome</em> we can, in about a week.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/905/1*54Q5kJ7M5FLRQ0_kYr72FQ.png" /></figure><p>How can our technology make a difference? Can we expand upon our platform to transform mundane tasks and even make complex tasks more efficient? Our application services for mobile apps and back office tools include a variety of software choices, we experimented to discover where we might fill gaps and expand our capabilities. We asked ourselves… Can we do better with data? Can our complex and automated solutions get faster and more accurate? Do we have any use cases that surround our existing solutions that we should explore? What can we quickly prototype or even build a MVP for? What’s missing in our office? (Lets build an app for that.)</p><p>At the end of the week we demoed 🎬</p><ul><li>😲 Improvements to streamline our visitor sign-in with TouchId/FaceID.</li><li>🏭 A new application for tracking facilities maintenance tasks.</li><li>🚧 An icon repository for our graphic library.</li><li>🎡 A queues app for managing a wait list.</li><li>🗣 Improvements for accessibility of our dashboard application.</li><li>🎛 Machine learning to leverage patterns for improving OCR recognition.</li><li>🗓 Event registration and sign-in.</li><li>📷 Facial recognition capabilities to by-pass manual checkin.</li><li>🚢 A new deployment solution for our dashboard application.</li></ul><p>…what a great demo this was 👏</p><p>A quote coined while demoing our A11y improvements:</p><blockquote>“Workplaces must be accessible. So too must workplace technology.” <br>— <a href="https://twitter.com/steveszc/status/1030564331006517248">Steve Szczecina</a></blockquote><p>A few screenshots from our live demo to close out the week…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/894/1*w9bcefRLruMZ90o0Aj7_og.png" /><figcaption>iOS app to fixit</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/864/1*pfoad0a9lFvIW-4R-GTUBg.png" /><figcaption>Exploring machine learning</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/641/1*39TYG2Hj1YDeGKJes5vcsg.png" /><figcaption>Facial recognition app</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/907/1*e1Bfd1FdENLqTPGFSgM2rg.png" /><figcaption>Event registration app</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/904/1*bf1BruihLIJmgWzXAUDRew.png" /><figcaption>Learning from data to improve features</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/907/1*BFAvi1ivaZQ5mg3xWhJdqA.png" /><figcaption>Queues app sending live text</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/865/1*Gi4KMWikASO6pdW5DWx29g.png" /><figcaption>Icon library</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/792/1*92DGio2F8lGihOfY0UIyVg.png" /><figcaption>Deployment app to activate a release</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/907/1*mOwdi35XOXIE4VYg8ipwJQ.png" /><figcaption>Using face ID</figcaption></figure><p>As the week finished, a few questions arose…</p><ul><li>What can we ship?</li><li>What can we use internally?</li><li>Which ideas can be segmented for beta testing by an existing customer, or even a new customer?</li><li>How can we make work always feel like a hackweek?</li></ul><p>We do not have all the answers yet, only time will tell. One thing we know for sure is — we want to do this again :)</p><p>When we take action through experimentation we discover that we can make the impact we strive for, to make workplaces <strong>better</strong>, <strong>safer</strong>, <strong>more fun</strong>, and do so <strong>with style</strong>. We will able to ship some of the internal projects so that we may learn more; and iterate. Also, we made plans to iterate upon some of the prototypes we demoed.</p><p>Since we innovate for the office, we strive to solve problems that meet our own interests. Likewise, we strive to develop solutions for the challenges that our customers experience. We are a <a href="https://envoy.engineering/building-our-remote-distributed-engineering-culture-2cfe9721ab4b">distributed</a> company, meeting together for our hackweek and demoing solutions was a rewarding experience. Our team had plenty of fun when we all met up at our headquarters in San Francisco, and it was a pleasure to connect and work together onsite. We rolled up our sleeves and built solutions not only for our own distributed office 🏢 but also for the workplaces of the tomorrow 🚀. We would love to ship some of the prototypes that we presented at the close of our hackweek — time will tell which become winning solutions as we iterate 📐and experiment ⚗.</p><p>A few observations from our experience hacking together…</p><p><em>What were your initial expectations for the hackweek?</em></p><p>- I figured that it would be fun 🎪, but was uncertain if we’d finish anything. What happens if we get pulled back on an issue for ongoing work? I was optimistic that we could build something cool to demo at the end of the week. I was not sure yet if I would have something valuable to contribute as a new member on the engineering team but hopeful to make a contribution.</p><p>- Building a half-working product that is 🐝 buggy and unusable.</p><p><em>What were the highlights, what stood out?</em></p><p>- The fact that all week long, ceremonies for organizing work were not really needed, we met as needed and at impromptu times of the day. We skipped formal processes in favor of just organizing ourselves. There was plenty to demo at the end of the week. I really liked the fact that many of the projects were something that we could actually put into play after the week was over. I was impressed by a mobile solution that simplified the steps users had to take by using features baked into the OS which created a streamlined and more natural experience. The efforts made on the dashboard project to apply changes to make the application more accessible to users was great, a demo with voice-over 🎙 helped us understand the experience of users who rely on voice over features to use a web application.</p><p>- Being able to see potential of our teams — building 🏗 a new features in a week, explore different solutions for a goal.</p><p><em>What made you laugh?</em></p><p>- During a demo an engineer mentioned “when you have a problem…” and at the same time the camera 🎥 used in the demo was pointed at a specific individual, the presenter started giggling and the crowd broke out in laughter 🤣. Not because the person was problematic, instead that as a group the team already knows how to work through problems and this individual proves that point.</p><p>- 🐶 Dogs in the office. :D</p><p><em>What did you enjoy about the demos, or team effort?</em></p><p>- I was fortunate to be on a team with a designer, product lead, API engineer and an iOS engineer. I contributed to the user interface portion of the feature. I was impressed that, as a newly formed team, we were able to identify the specific outcome we wanted to demo — even an ambitions scope of work. We were able to demo a full experience that involved coordination between multiple devices 📱 and users. It was fun to explore the possibilities of what we could deliver as a prototype in only a few days. As a new engineering team member, it was a pleasure to work with people I have not yet had the privilege to work aside yet. I enjoyed the collaboration with individuals from our product and design teams. The variety of demos was impressive, from internal tools to solutions our which customers may benefit from.</p><p>- To see how far our team can go in one week.</p><p><em>Where there any risks you took and/or challenges you faced?</em></p><p>- I thought it was pretty risky to take on the scope of the prototype that we wanted to demo/present in only a few days. Along with other <em>all-hands</em> 🙌 activities 🍻 🍷 🍩 🍹 🎉 occurring the same week - we only had a few days of focus. I tried out a library for a small mobile web app which I had not used before. It appeared to have the tooling I needed to move quickly and provided testing as well (I favor test driven development). Our designer did a great job, I knew I may not have the time to apply all the polish that would make the design really shine. But I made the effort to keep up with the iterations of the designs; while also developing a solution.</p><p>- Not being able to socialize as much as want to for all-hands week because I was focusing on hackweek project 🚧.</p><p><em>What did you learn?</em></p><p>- I learned that the leadership team values experimentation and discovering solutions for the workplace; even by looking for solutions in our own environment 🕵️‍. I learned that talented people will do the right things to reach their goals, they will collaborate and have fun while taking on an ambitious goal. I learned how to use Glimmer.js for a small mobile web application and also tried out Tailwind CSS, a utility first library.</p><p>- How to build an iOS app.</p><p><em>What did you take away from the experience?</em></p><p>- I would like to ship 🛳 the project we worked on, I know it was only a prototype but I think we did show a viable solution. I’ll lean on our product team to know if that solution is also desirable for our customer base or even new customers. It was a pleasure to see the innovation that resulted from dedicating a week toward hacking on ideas brought up throughout the engineering, product and design teams. I would enjoy doing this again. I think I also have less reliance on the ceremonies we keep for producing software and instead a stronger reliance on the people to do so. We pulled off a nice prototype/demo without any traditional agile ceremony, just ad-hoc meeting and syncing up as questions arose.</p><p>- Learning new things and being able to work with different people 👾 👩‍🚀.</p><h4>In Closing…</h4><p>Hopefully sharing our hackweek experience can provide some insight into the kind of people we employ at Envoy, as well as the values we hold. It’s a pretty accurate snapshot 🖼 of our internal culture that we hope inspires other teams to experiment, to create margin for innovation, and to attract like-minded product owners, designers and software engineers to work with too.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bd47dd0441f8" width="1" height="1" alt=""><hr><p><a href="https://envoy.engineering/hackweek-2018-recap-bd47dd0441f8">Hackweek 2018 Recap</a> was originally published in <a href="https://envoy.engineering">Envoy Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A framework for ambitious Chrome Extensions]]></title>
            <link>https://envoy.engineering/a-framework-for-ambitious-chrome-extensions-b08d1f4b944d?source=rss----d8aa51f79fb---4</link>
            <guid isPermaLink="false">https://medium.com/p/b08d1f4b944d</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[emberjs]]></category>
            <category><![CDATA[chrome-extension]]></category>
            <category><![CDATA[engineering]]></category>
            <dc:creator><![CDATA[Esteban]]></dc:creator>
            <pubDate>Mon, 06 Aug 2018 16:54:24 GMT</pubDate>
            <atom:updated>2018-09-26T17:22:56.952Z</atom:updated>
            <content:encoded><![CDATA[<p>At Envoy, we love Ember.js. We enjoy coming to work every day and not worrying about set-ups, naming things, or doing repetitive tasks. We love <em>convention over configuration</em> (yes, we also use Ruby on Rails). The Ember ecosystem is great, full of amazing <a href="https://www.emberaddons.com/">add-ons</a>, a wonderful community, a strong testing culture, and one of the greatest <a href="https://ember-cli.com/">command line interfaces</a>. That’s why every time we need to build something new on the front-end we ask ourselves, can we use Ember here? Is it the right tool for this problem? Sometimes the answer to that question is no (we also use React); but usually it is <strong>yes</strong>. Besides, using Ember makes it so easy to transfer knowledge between different projects and we have now nine engineers working almost full-time with Ember.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*Im2vmwrsdnGiOgMNrCqUwA.gif" /><figcaption>Source: <a href="https://dribbble.com/shots/1996259-Swiss-army-knife">https://dribbble.com/shots/1996259-Swiss-army-knife</a></figcaption></figure><p>This time we needed to build a Chrome extension, so we asked that question again; the answer ended up being… “maybe”. There are a ton of great <em>starter</em> and <em>boilerplate</em> projects on Github for building extensions, but we found out that with <a href="https://ember-cli.com/">ember-cli</a> and the right mix of add-ons, it would be more than enough to have a solid environment to work with.</p><p>So here I’m going to share the path we took to build a Chrome extension using Ember.js and the things we would have wanted to know a couple weeks ago.</p><h3>Final app</h3><p>Some context of what wanted to build: we needed a single Chrome extension with multiple components sharing a <strong>single</strong> Ember code-base. These extension components were:</p><blockquote><a href="https://developer.chrome.com/extensions/browserAction"><strong>Browser/Page Action popup</strong></a><strong>:</strong> A small window that’s opened with a click on the corresponding UI element.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/439/1*c5w25F71yLQeAGCnYn-GGw.png" /></figure><blockquote><a href="https://developer.chrome.com/extensions/content_scripts"><strong>Content Scripts</strong></a><strong>:</strong> A script that runs alongside pages; for compatibility reasons, it <a href="https://developer.chrome.com/extensions/content_scripts#execution-environment">runs in an isolated world</a>, but it shares the DOM with the page, and as such can modify it.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CEpN6DCBM2mMtSyOp940ew.png" /></figure><p>One of the coolest parts of the <em>Content Scripts</em> isolated world is that you can inject any library or code into a website and not worry about any conflicts with existing JS running there. You can have different versions of frameworks or trigger different events, plus you can share the DOM 🔝.</p><h3>Initial setup and configuration</h3><p><a href="https://github.com/rmachielse/ember-cli-deploy-chrome-app">ember-cli-deploy-chrome-app</a> is the initial boilerplate for an Ember Chrome extension and the easiest way to integrate CI and all the deployment processes to the Chrome Web Store.</p><blockquote>It generates a chrome folder and a key.pem file in the root of your project. In chrome you&#39;ll find background.js and a default manifest.json that you can change according to your needs.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/444/1*jM-2u7Q1DUnIyT7S06qlDg.gif" /></figure><p>This chrome folder will have mostly configuration files and references to our Ember app. It also includes some handy symlinks pointing to the dist folder to access those files.</p><p>⚠️ Because we will need a <em>Content Script </em>extension component<em> </em>(which injects scripts into a page), symlinks do not work in this scenario. So every time the Ember app is built, we need to copy files from dist into chrome.</p><p>Obviously there is an add-on for that: <a href="https://github.com/tgsoverly/ember-cli-post-build-copy">ember-cli-post-build-copy</a></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9dbb09352808c593d9724d746494e237/href">https://medium.com/media/9dbb09352808c593d9724d746494e237/href</a></iframe><p>You may have noticed we’re only copying two files here, app.js and app.css. But a normal Ember app has also a vendor.js and avendor.css. We used <a href="https://github.com/tgsoverly/ember-cli-post-build-copy">ember-cli-concat</a> to have a single file of JS and CSS so we can manage them more easily.</p><p>We won’t have an index.html here either. Therefore, we have to turn off <a href="https://ember-cli.com/user-guide/#application-configuration">storeConfigInMeta</a>, so that all the config data is included in the app.js.</p><p>Finally our ember-cli-build.js will look something like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6f67396ee99befc781f5528d0fa724bc/href">https://medium.com/media/6f67396ee99befc781f5528d0fa724bc/href</a></iframe><h4>Booting up the Ember app</h4><p>In order to share a single code-base with these extension components, we needed to manually control the booting of the application. Specifically for the <em>Content Script</em> part, where we want to wait until the user performs an action to render the app.</p><p>We can easily configure it by setting autoboot: false and calling</p><pre>require(&#39;chrome-extension/app&#39;)[&#39;default&#39;].create({ rootElement: &#39;#selector&#39; });</pre><p>later when needed.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5792808378a3eb7e7d68beca1c9d2d72/href">https://medium.com/media/5792808378a3eb7e7d68beca1c9d2d72/href</a></iframe><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b419d6e61baf5391f9c97de325d05c13/href">https://medium.com/media/b419d6e61baf5391f9c97de325d05c13/href</a></iframe><p>⚠️ We need to make sure we’re loading the JS files in the right order, so we can have access to require(&#39;chrome-extension/app&#39;)[&#39;default&#39;]</p><pre>// chrome/manifest.json</pre><pre>&quot;content_scripts&quot;: [<br>  {<br>    &quot;matches&quot;: [&quot;<a href="https://calendar.google.com/calendar/*">https://website/where-i-will-run-this-code/*</a>&quot;],<br><strong>    &quot;css&quot;: [&quot;assets/app.css&quot;],<br>    &quot;js&quot;: [<br>      &quot;assets/app.js&quot;,<br>      &quot;content-script.js&quot;<br>    ],</strong><br>    &quot;run_at&quot;: &quot;document_idle&quot;<br>  }<br>],</pre><blockquote>If you want to learn more deeply about how an Ember app boots, I highly recommend:</blockquote><p><a href="https://hackernoon.com/how-does-ember-boot-5e1f9e7a1117">How does Ember Boot?</a></p><p>Because we set autoboot: false, we also need to manually boot up the Ember app on the <em>pop up</em> side.</p><pre>// chrome/manifest.json</pre><pre>&quot;browser_action&quot;: {<br>  &quot;default_icon&quot;: {<br>    &quot;16&quot;: &quot;images/logo-icon-16.png&quot;<br>  },<br>  &quot;default_title&quot;: &quot;Ember&quot;,<br>  <strong>&quot;default_popup&quot;: &quot;popup.html&quot;</strong><br>},</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d585690d729cff639c33ff2afe0e4aa9/href">https://medium.com/media/d585690d729cff639c33ff2afe0e4aa9/href</a></iframe><p>Lastly, we need to know where we are booting the Ember app from, so we can know which logic to use or which components to render. For this, we add a &lt;meta&gt; tag to the popup.html file, so can get its content from the <em>application controller</em>.</p><pre>// popup.html</pre><pre>&lt;meta scope=”ember-app-from” content=”popup”&gt;</pre><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e649fc6f97213d653e2eaa19b897b117/href">https://medium.com/media/e649fc6f97213d653e2eaa19b897b117/href</a></iframe><h4>Working with the Chrome API inside an Ember app</h4><p>Access to the <a href="https://developer.chrome.com/extensions/api_index">Chrome extension API</a> is available inside the Ember app from any file at any time. You can either use /* global chrome */ on files where you’re using it or just add it to your .eslintrc.js file so ESLint doesn’t yell at you.</p><pre>// .eslintrc.js</pre><pre>module.exports = {<br>  globals: {<br>    $: true,<br>    <strong>chrome: true,</strong><br>    window: true<br>  }<br>}</pre><p>One of the APIs we needed was <a href="https://developer.chrome.com/extensions/storage">chrome.storage</a>. We use it to save and share the “session” between the <em>popup </em>component<em> </em>and the <em>content script. </em>It provides the same storage capabilities as the normal localStorage API that you would have access to on any web app.</p><blockquote><em>When using </em>storage, <em>the </em>extension’s content scripts<em> can directly access user data without the need for a background page.</em></blockquote><p>To use almost all of these APIs you need request permission when the user is installing the extension. Just specify these permissions in your manifest file.</p><pre>// chrome/manifest.json</pre><pre>&quot;permissions&quot;: [&quot;tabs&quot;, &quot;activeTab&quot;, &quot;storage&quot;, &quot;management&quot;]</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/512/0*yysB4Dh_FJK2Cnw6.jpg" /></figure><p>One of the must-install add-ons in any Ember app is <a href="http://ember-concurrency.com/docs/introduction/">Ember-Concurrency</a> (EC), as it makes it so easy to write concise, robust, and beautiful asynchronous code. We certainly wanted to use it here. But <a href="https://developer.chrome.com/extensions/api_index">Chrome extension API</a> uses a callback design structure API. In order to convert all these “callback hell” functions into something we can plug-in inside any EC task we needed: <a href="https://github.com/tfoxy/chrome-promise">chrome-promise</a>. With it, we canyield any call to the Chrome extension API.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/64d0d661d13c19f07f46097bd8cbdb27/href">https://medium.com/media/64d0d661d13c19f07f46097bd8cbdb27/href</a></iframe><h4>Installing the Chrome extension locally</h4><ul><li>Open the Extension Management page by navigating to <a href="chrome://extensions">chrome://extensions</a>.</li><li>Enable Developer Mode by clicking the toggle switch next to Developer mode.</li><li>Click the LOAD UNPACKED button and select the extension directory (/chrome).</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1020/1*vdSo0q6xD1lt15eKIvgCow.gif" /></figure><h4>Live reloading</h4><p>⚠️ Because we’re in an encapsulated environment, the WebSocket connection created by <em>ember-cli-inject-live-reload </em>won’t work here. Instead we need to watch for file changes under thechrome folder. For this we can use <a href="https://github.com/xpl/crx-hotreload">https://github.com/xpl/crx-hotreload</a>. When a change is detected, it reloads the extension and refreshes the active tab (to re-trigger the updated scripts).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LwZE0DUWPv1-PySfqPT9BA.gif" /></figure><h4>🚨 Known limitations</h4><ul><li>Chrome apps currently seem unable to handle HTML5 pushState. Make sure locationType is set to hashin your config/environment.js file.</li><li>Depending on your configuration, you might need to disable fingerprinting. If you have symlinked chrome/window.htmlto dist/index.html, the asset urls in app/index.html will be compiled like normal. However, if you have a different window.html, it will not be compiled and thus will unable to handle fingerprinted assets. In that case you have to disable it in ember-cli-build.js by setting fingerprint to { enabled: false }.</li></ul><h4>Conclusions</h4><p>We felt really confident working and publishing this extension, it has a good test coverage, it shares the same patterns we use on our main Ember app and we know that any engineer can jump on it and add new features or fix bugs. We’re basically speaking the same language as if we were working on a normal web app. At the end an Ember app is simply a .js file plus a .css, so with the correct configuration you can basically port it anywhere you want.</p><p>Finally, by sharing this, we hope to encourage more people to have Ember.js as an option when creating Chrome apps. The Chrome API and the Ember.js ecosystem is a powerful mix for building <em>ambitious</em> <em>Chrome</em> <em>extensions</em>.</p><h3>(((Ian Irving)) 🍁 on Twitter</h3><p>@esbanarango always forget that #EmberInspector is a (brilliant) Ember app and Chrome extension that I use every day. #EmberJs</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b08d1f4b944d" width="1" height="1" alt=""><hr><p><a href="https://envoy.engineering/a-framework-for-ambitious-chrome-extensions-b08d1f4b944d">A framework for ambitious Chrome Extensions</a> was originally published in <a href="https://envoy.engineering">Envoy Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building our (remote, distributed) engineering culture]]></title>
            <link>https://envoy.engineering/building-our-remote-distributed-engineering-culture-2cfe9721ab4b?source=rss----d8aa51f79fb---4</link>
            <guid isPermaLink="false">https://medium.com/p/2cfe9721ab4b</guid>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[remote-working]]></category>
            <category><![CDATA[culture]]></category>
            <dc:creator><![CDATA[Seth Sakamoto]]></dc:creator>
            <pubDate>Fri, 18 May 2018 19:50:24 GMT</pubDate>
            <atom:updated>2018-05-19T15:17:03.436Z</atom:updated>
            <content:encoded><![CDATA[<p>We’re proud of our engineering culture at <a href="http://envoy.com">Envoy</a>. We’re also proud of the fact that 50% of our team works from their homes (the balance is in our San Francisco HQ) and we feel like a unified, high-functioning team.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XHzz6REdbfKCfMm_Gi7KWQ.png" /><figcaption>We’re actually required to wear these all the time</figcaption></figure><p>Note: this isn’t about how you build a multi-location engineering org, meaning teams located in different geos. In my experience the tactics to make that work are different. This is about building a team composed of folks in different locations.</p><p>Here are the things we do that make it work for us and keep it going as we hire more folks. We hope these ideas can help you too.</p><h3>It starts with your goals and mindsets</h3><p>The biggest factor is how our team thinks of our distributed environment. Do we hire folks outside of San Francisco because they’re cheaper? Do we believe being in the same room is unilaterally better for communication? Are we doing this to compete for the best talent?</p><p>Goals and mindsets affect your approach, strategy, and day to day behaviors so it’s important to be clear, explicit, and on the same page about them.</p><p>For us, we do it simply because we’re out to hire the best, and don’t think the best only lives in San Francisco. The talent pool in the world is only getting larger with the evolution of the industry.</p><p>The mindsets that help us strategically and day to day:</p><ol><li><strong>Everyone is a 1st class citizen.</strong> We prefer the word “distributed” over “remote” as there’s no implied hierarchy.</li><li><strong>Communication is critical. </strong>The biggest difference between working colocated with someone and not is the communication channels can be different. It never means that communication is somehow less important.</li><li><strong>We hire and value folks that can succeed in a distributed environment.</strong> It’s not for everyone, and not everyone will be good at it.</li><li><strong>A strong engineering culture &gt; distributed culture. </strong>We believe that promoting shared values, mindsets, skills, and behaviors in our people promotes fairness, regardless of location (and ethnicity and gender and sexual orientation and age and …).</li></ol><h3>We hire and construct our team around these goals &amp; mindsets</h3><p>If the goals &amp; mindsets are the “what”, the “how” is in part about who you hire and how you set your teams up to work. These are the guidelines we use for hiring and constructing teams:</p><ol><li>We hire folks primarily <strong>in or near our timezone</strong> (our specific guideline is continental US timezones) so folks can more easily collaborate real time. Timezones cause more pain than location in that regard. If we go into other timezones it will be with the intent of constructing a self-sufficient team there but right now we don’t want or need to.</li><li><strong>50/50 split between HQ &amp; distributed</strong> is our target, though that’s flexible. This is because much of our company is still HQ bound, so it’s important to have a significant enough presence in HQ.</li><li><strong>Balance</strong>. It’s more ideal when there’s a good balance of senior folks working in and outside of HQ, so a location doesn’t have an implicit importance. Similarly, we try and avoid situations where there is a lone person working from home on a team. This should be a lagging indicator of our approach, but is nice when it’s achieved. 2 of our 4 managers and half our senior engineers work out of their home, for example.</li><li><strong>Evidence of success working from home.</strong> This usually means we won’t hire junior/early-career folks that work out of their house, and we’ll be extra judicious with experienced folks working from home full time for the 1st time. This might change as we get bigger and more able to afford the time to work kinks out.</li><li><strong>Empathy, “High I/O” (good communication and open mindedness), strong accountability</strong> … basically we hire to the desired behaviors in our <a href="https://github.com/envoy/Engineering/blob/master/engineering_bands.md">job bands</a>. This enables our culture everywhere.</li></ol><p>Note: as mentioned earlier, these are tactics for constructing teams made up of folks distributed across locations. At a certain size, we’ll probably mix in having entire teams at locations or perhaps teams made up entirely of folks working from their homes. We’ll evolve our tactics then.</p><h3>How we work</h3><p>It’s not just how we hired and constructed our teams. We also put in systems to ensure it works day to day.</p><ol><li><strong>Overcommunicate everywhere</strong>. This works whether we’re talking to someone a few desks away or in another city. We’ve focused a bunch of energy on great Github etiquette (verbose, templated pull requests and reviews), task tracking (Jira), heavy use of Slack and video, recording meetings if folks can’t attend, ceremonies like weekly demos/Show &amp; Tells and all-engineering gatherings, and many more things. Related, we invest in the highest quality tools for collaboration (Zoom is pretty nice). It pays off.</li><li><strong>Nothing beats face to face for bonding. </strong>No matter what we do, you can’t replace the human connection which is best created face to face. To this end, we have a quarterly co-location, usually at SF HQ, and loose budgets for traveling in between. That way we spend at least a month a year face to face if not more.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oZjsnuc90vEtvHwTRl0UDg.png" /><figcaption>Our recent all-company get together</figcaption></figure><h3>Conclusion</h3><p>We believe this is a template we can follow at scale. We feel as long as we have teams composed of folks distributed across locations, we will be able to perpetuate our success using this formula. We hope this helps you too!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2cfe9721ab4b" width="1" height="1" alt=""><hr><p><a href="https://envoy.engineering/building-our-remote-distributed-engineering-culture-2cfe9721ab4b">Building our (remote, distributed) engineering culture</a> was originally published in <a href="https://envoy.engineering">Envoy Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[iBeacon Bug: Hunting for a needle in a haystack]]></title>
            <link>https://envoy.engineering/ibeacon-bug-hunting-for-a-needle-in-a-haystack-fc87f6c56b4b?source=rss----d8aa51f79fb---4</link>
            <guid isPermaLink="false">https://medium.com/p/fc87f6c56b4b</guid>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[ibeacon]]></category>
            <category><![CDATA[debugging]]></category>
            <category><![CDATA[ble]]></category>
            <dc:creator><![CDATA[Fang-Pen Lin]]></dc:creator>
            <pubDate>Fri, 12 Jan 2018 17:29:03 GMT</pubDate>
            <atom:updated>2018-01-15T18:46:01.722Z</atom:updated>
            <content:encoded><![CDATA[<p>Envoy has already signed in more than 17 million visitors around the world. To help frequent visitors, we built <a href="https://envoy.com/passport/">Envoy Passport</a> with <a href="https://en.wikipedia.org/wiki/Bluetooth_Low_Energy">BLE</a> technology. When you approach the iPad app, it detects BLE signal and notifies the iPad you’re here, like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1l49RMgjySqs5aLAZZw29A.png" /></figure><p>This feature may look trivial, but it’s not! There was a bug here that took us several months to track down. Today we are going to share the story of hunting the bug like finding a needle in a haystack.</p><h3>It just works! Until it doesn’t 😅</h3><p>After we launched the Passport app, some users reported that it occasionally didn’t work. The most difficult bugs to find aren’t those ones that happen all the time, but the ones that happen sporadically.</p><p>Besides that, there were even more factors that made this bug harder to find:</p><ul><li>It involved real devices interacting with each other, and it couldn’t be reproduced on a simulator.</li><li>It was hard to tell if the iPad or iPhone app was causing the problem.</li><li>Because iBeacon is facilitated by iOS, we didn’t know whether it was a bug from iOS or our app.</li><li>The Passport app itself is very complex.</li></ul><p>Given the massive scope and factors, this wasn’t an easy bug to fix. Here’s how we hunted the bug down step by step.</p><h3>Round 1. Simplify</h3><p>At the very beginning, we had no clue whether it was Apple’s bug or ours. Either way we realized that finding out was our first priority. If it’s Apple’s bug, we can only report and wait for them to fix.</p><p>Passport is not a simple app, it talks to an API server, gets data from it, caches it, it updates the UI, and responds to user interaction. There are too many factors involved. And just like what Sherlock’s author said</p><blockquote>Once you eliminate the impossible, whatever remains, no matter how improbable, must be the truth</blockquote><blockquote>— Arthur Conan Doyle</blockquote><p>To make it easier for us to find out the truth, we need to eliminate as much of the impossible as possible up front. Say if it’s really a bug in iOS, then we should be able to reproduce the glitch with only simple BLE functionality, all other functionalities are just noises. So the strategy we adopted here is to simplify.</p><p>We built very simple apps, one for iPad and one for iPhone. They do nothing but BLE signal broadcasting and monitoring. The iPad app has an “I’m here!” button in it like this</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rD03plxbEr4MIrKg-5tLEg.png" /></figure><p>We set it up at the front desk and asked our co-workers to help. They install another simple app on their iPhone, and every day they come to work, they tap the button and select their name to sign-in. Now we have simple apps which do only BLE broadcasting and listening. Next, to know the truth, we need to collect data.</p><h3>Round 2. Logs, logs, and more logs</h3><p>As an engineer, I loved to watch a TV show when I was a kid — <a href="https://en.wikipedia.org/wiki/Mayday_(Canadian_TV_series)">Air Crash Investigation</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*7mqdJarn5Jkusqhpt2pWKQ.jpeg" /></figure><p>It’s a TV show about the investigation of airplane crashes. There are many invaluable engineering lessons to learn from a tragedy. One very important lesson I’ve learned from this TV show was: write logs.</p><p>When an airplane crashes and the <a href="https://en.wikipedia.org/wiki/Black_box">black box</a> gets lost, it’s very hard to find out what happened. Software also runs and crashes — that’s why we should keep logging what’s happening.</p><p>To know what’s going on with the BLE stack, We started to log relevant data for our investigation — like</p><ul><li>Start broadcasting signals</li><li>Entering an iBeacon region</li><li>Exiting an iBeacon region</li><li>… so and so on</li></ul><p>We could then read and investigate that data to see why the BLE was not working as expected.</p><h3>Round 3. Centralize the log</h3><p>To make it easier to investigate on a larger scale, we developed a simple API server that writes BLE activities event log into a database.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0GdURRoouMfGsH35bCu9wQ.png" /></figure><p>Now, we are in the big data business! With all the events available in a database, we can query and find exactly the pattern we are looking for.</p><h3>Round 4. Suspect found, but not the murderer</h3><p>With the BLE data, we soon quickly identify and confirm a bug in iOS BLE stack, we published the report <a href="https://github.com/envoy/ibeacon-experiment-report">here</a>. We found out that iBeacon region stops monitoring after the device reboots sometimes. Unfortunately, this doesn’t look like the major root cause of the unstable BLE issue, since people don’t usually reboot their iPhone. We reported this bug to Apple, they fixed it and we kept looking.</p><p>To know why sometimes it works, or doesn’t work, we need more details, like</p><ul><li>When did the user enter the building?</li><li>When do they take out their iPhon?</li><li>When do they tap the sign-in button?</li><li>How do they carry their iPhone</li><li>Do they put it in a metal box that blocks radiation signal or what?</li></ul><p>To find out, we look into office security camera video footage.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QuMgp_H2T1AMUTWgJ7f5kQ.png" /></figure><p>After a long time of data collection and analytics, we came to a conclusion:</p><p>There are few iPhones seem to be malfunctioning, tend to not respond to iBeacon signals, but for almost all iPhone devices always work as expected. It may sound like we can easily jump into it’s the flaw of few devices as the final conclusion, but the problem is, some of our co-workers reported their passport is not working, and their device actually always works with the simple experimental app.</p><blockquote>Hmmm, okay…. 🤔</blockquote><p>Sorry, there is no conclusion yet. But at least for now, we can stop blaming Apple. We know there must be a bug in our code, or at least something different from the simple BLE app causes the malfunctioning.</p><h3>Round 5. Sysdiagnose is here to help!</h3><p>We contacted Apple for help. Apple asked us to install Bluetooth log profile and run sysdiagnose. We followed the instructions and found out that the sysdiagnose tool is super helpful for investigating the root cause of the bug, as it logs detailed events about Bluetooth activity in OS level.</p><p>To run sysdiagnose, visit <a href="https://developer.apple.com/bug-reporting/profiles-and-logs/">this page</a>, find the functionality you would like to debug, it’s Bluetooth for iOS for our case, we download the profile and install it on our iOS devices. Normally iOS won’t bother to log too many details, but this profile enables it. Next, use your iOS device as normal, and when encountering the problem, you long press</p><pre>Volume Up + Volume Down + Power</pre><p>and release. You will feel a short vibration, which means iOS started collecting sysdiagnose logs for you. Wait few minutes, then plug in your iOS device to your Mac, use iTunes to sync it, and you should be able to find the sysdiagnose log file at</p><pre>~/Library/Logs/CrashReporter/MobileDevice/[Your_Device_Name]/DiagnosticLogs/sysdiagnose</pre><p>Then you can open the “system_logs.logarchive” file with the Console app and you should be able to see the logging entries popping up</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zeqNXGJAn5McrYDtESiQ-A.png" /></figure><p>Once all logging entries are loaded, you can scroll to the time that specific issue occurred, and see if there is anything strange.</p><h3>Caught in the act!</h3><p>With the help of sysdiagnose, we finally found something promising. We realized that iOS was doing a great job in terms of seeing the iBeacon region.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/879/1*8VbbYLxYRF2ssx6PLrYOIg.png" /></figure><p>After entering the iBeacon region, iOS should launch the Passport app and notify it about this event. However, instead of our app being notified, we found a dead body:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/985/1*p-fkDro2pASNpzges6syEQ.png" /></figure><p>Yep, that’s our app and it crashed! After looking into the crashing report, we found that its error code is 0x8badf00d (bad food), we googled and found out it means the app takes too long time to launch and not responsive, thus end up getting killed by an iOS demand process call watchdog.</p><p>With that in mind, we look into how the app launches and realized that it actually takes a long time to load your previous sign-ins from RealmDB when you have many of them. That’s a pretty ironic finding, the more you use the app, the more likely it’s not going to work. Fortunately now we know and caught this serial killer in the act!</p><h3>Solution</h3><p>As the app got killed for taking a long time to launch for iBeacon event, we decided to make launch as lightweight as possible, we change the code a little bit by looking into “UIApplicationLaunchOptionsLocationKey” in “launchOptions”</p><pre>- (BOOL)application:(UIApplication *)application <br>   didFinishLaunchingWithOptions:(NSDictionary *)launchOptions<br>{<br>    if (launchOptions[UIApplicationLaunchOptionsLocationKey]) {<br>        // Do not load UI and any other heavy duty loading stuff<br>        // Kick start iBeacon here<br>        return YES;<br>    }<br>    // ...<br>}</pre><p>So if the app is launched for a location event, then it only runs necessary code for iBeacon setup. Since we’ve fixed the problem, Passport now always works like a charm!</p><h3>Takeaways</h3><p>That’s a long story to tell, but actually, there are still some details not covered. To keep it short, here are some lessons we’ve learned:</p><ul><li>If your app takes too long time to launch and not responding, it may get killed by watchdog, and of course, your app won’t see the iBeacon event</li><li>If your app launches in short time but didn’t setup iBeacon in time, it’s also possible your app also won’t see the iBeacon event</li><li>If your app happens to enter a region and another with the same UUID, it won’t be notified even the major or minor number is different, as iOS only cares about the region UUID when monitoring. And it only notifies your app when it enters or exits a region.</li><li>App running with TestFlight may take longer time to launch, as it seems iOS needs to talk to server to ensure the testing is still valid</li><li>Avoid heavy-duty loading when app is waking up for a location event</li><li>Setup iBeacon as soon as possible when the app is launched for a location event</li><li>Writing logs about software activity is a good practice, it helps a lot while debugging</li><li>Simplify the code as much as possible to reproduce a very hard to reproduce bug</li><li>Luke, use sysdiagnose!</li></ul><p>Finally, hope you enjoy our bug hunting story. If you’re like us, feel excited about finding the root cause of bizarre bugs, strive to build great products and pursue it-just-works quality, <a href="https://envoy.com/jobs/">Envoy is hiring</a>!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fc87f6c56b4b" width="1" height="1" alt=""><hr><p><a href="https://envoy.engineering/ibeacon-bug-hunting-for-a-needle-in-a-haystack-fc87f6c56b4b">iBeacon Bug: Hunting for a needle in a haystack</a> was originally published in <a href="https://envoy.engineering">Envoy Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How data helps us design our products]]></title>
            <link>https://envoy.engineering/how-data-helps-us-design-our-products-726a2ce82ae6?source=rss----d8aa51f79fb---4</link>
            <guid isPermaLink="false">https://medium.com/p/726a2ce82ae6</guid>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[product-design]]></category>
            <category><![CDATA[product-management]]></category>
            <dc:creator><![CDATA[Ben Hu]]></dc:creator>
            <pubDate>Fri, 08 Dec 2017 02:13:04 GMT</pubDate>
            <atom:updated>2017-12-08T02:13:03.217Z</atom:updated>
            <content:encoded><![CDATA[<p>“How can we collect more data?”</p><p>“Let’s look at the data.”</p><p>“It would be great to get some data around how users are using feature <em>x</em>.”</p><p>These are utterances we hear at Envoy quite often, and everywhere it’s becoming ever more common with companies collecting and consuming more and more data. Our goal, however, has not changed too much — we want to identify clear trends in the data that can help us make better product decisions about <em>what</em> to build, <em>how</em> to best build it, and for <em>whom</em>. As we grow with our product teams, becoming multi-product, exploring more complex use-cases, and modernizing ever more offices, data becomes increasingly important and increasingly challenging to scale. In this post, we share some best practices we’ve found and lessons we learned.</p><h4>Analyzing usage is key</h4><p>Unsurprisingly, staying abreast of how your users are using your product is critical to success. Keeping up with usage allows you to identify problems faster, recognize changing trends in a timely manner, and prioritize features. Data helps keep our product teams honest and makes sure the right things are being built and results are tracked and monitored.</p><p>Developments in technology has really helped close the gap between us and our users. At Envoy, we use tools such as <a href="http://heapanalytics.com">Heap</a> and <a href="http://fullstory.com">Fullstory</a> to be able to meet our customers right where they using our product, in their environment. This not only helps eliminate bias from our user sessions, but also saves us time. We’re able to replicate the results of traditional UX research sessions for a fraction of the hours and cost, and scale as our number of users grow. Casting a wider net also helps us identify new use cases and workflows previously unanticipated. We can then spend more of our time understanding the findings to build empathy for use cases and pain points to help prioritize our roadmap.</p><h4>Having a hunch</h4><p>We’ve found product usage analytics has helped us think through a hunch. We often have hunches about how users get maximum value out of our product. It could be something reactive such as how successful adoption of a new feature is. It more often is forward-looking such as whether a series of actions or behaviours lead to surfacing more value. In times like this, it is less intuitive to get existing data to make a decision. But we’ve found that clustering users to find those who exhibit the hypothesis, or in some cases a correlated proxy action, gives us enough insight to set a direction. Hypothesis testing like this allows us to iterate quickly and choose a fork in the path, even if the path remains unclear.</p><h4>What data cannot do</h4><p>On the other hand, we are carefully pessimistic about what data cannot do as well. Data cannot innovate your product for you. Being data-driven is great for corroborating intuition, iterating quickly, identifying problems promptly, and making difficult decisions with no clear direction. It can optimize your current product and take you to a ‘local maximum’, but it will always be blind to the ‘global maximum’. Ultimately, to create the better office experiences of the future, a deep empathy for our users, the problems they are trying to solve, and a brilliant team of innovators are required to be successful.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*BM310wCdYyQ1oQXr." /></figure><p>Follow us for monthly updates on how we keep using data to enhance our product!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=726a2ce82ae6" width="1" height="1" alt=""><hr><p><a href="https://envoy.engineering/how-data-helps-us-design-our-products-726a2ce82ae6">How data helps us design our products</a> was originally published in <a href="https://envoy.engineering">Envoy Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Switching from marketing to analytics]]></title>
            <link>https://envoy.engineering/switching-from-marketing-to-analytics-ca18165a86a1?source=rss----d8aa51f79fb---4</link>
            <guid isPermaLink="false">https://medium.com/p/ca18165a86a1</guid>
            <category><![CDATA[data-science]]></category>
            <dc:creator><![CDATA[Jordan Stein]]></dc:creator>
            <pubDate>Thu, 23 Nov 2017 01:51:10 GMT</pubDate>
            <atom:updated>2017-11-23T01:51:10.258Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1014/1*kbd-NJYu7QxiweNuZh9goA.jpeg" /></figure><p>Last weekend at a Friendsgiving dinner I asked another guest, “What do you do?” In between bites of pumpkin pie, she replied in typical San Francisco fashion, “I work at a startup doing data ops, but it’s small so I really do a bit of everything. Very Series A.”</p><blockquote>“I do a little bit of everything.”</blockquote><p>This line seemed particularly relevant to me given my first six months at Envoy. In that short time span, I’ve had the opportunity to be part of both the Marketing and Data Science teams. This experience would be hard to find anywhere except at a small startup, and I’m very grateful for the perspective I’ve gained from working in two very different functions within the organization.</p><h4><strong>Why the switch?</strong></h4><p>As a recent college grad, and someone with a fresh perspective on marketing, it’s clear to me that the modern marketer must have an understanding of data and new technology to survive.</p><p>Hot Topics recently held a <a href="https://www.hottopics.ht/29530/unicorn-cmo-roundtable-part-2/">roundtable discussion</a> about the future of marketing with six senior marketing executives:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2F2Pc1BqYBu9o%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D2Pc1BqYBu9o&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2F2Pc1BqYBu9o%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/735bbbb1d345872c55f99e15ef137b9f/href">https://medium.com/media/735bbbb1d345872c55f99e15ef137b9f/href</a></iframe><p>When asked to describe what a B2B Marketing organization would look like in the future, the importance of data science dominated the discussion. Peter Thomas, Managing Director of Integrated Marketing at Accenture said of his marketing team, “It will have data scientists in it. We wouldn’t have had those two or three years ago, we wouldn’t have even known why we need them.”</p><p>Scott Allen, CMO of Microsoft UK describes the ideal member of his team as “part scientist or part creative, and those two need to come together. I’ve got a lot of data and insight professionals on my team, and I only see that being beefed up as it becomes the muscle of the CMO over the next couple of years. But there are other roles that have to be in there alongside that, which are the traditional creative roles.”</p><p>I’ve seen the new age of data-driven marketing embodied at Envoy. A prime example of the fusion of data and marketing is the tools we use to try and understand our customers. The parts of our tech stack that are especially pertinent to me as a marketer are:</p><ul><li><a href="https://heapanalytics.com">Heap Analytics</a> to track every action people take on our website and inside our product;</li><li><a href="https://clearbit.com">Clearbit</a> as a data enrichment tool to provide information about company size, industry, geography and job titles;</li><li><a href="https://www.intercom.com">Intercom</a> as a marketing automation and segmentation tool;</li><li><a href="https://segment.com">Segment</a> to bring data from all these sources together;</li><li><a href="https://www.stitchdata.com">Stitch Data</a> for ETL to send data to Amazon Redshift, our data warehouse.</li></ul><p>The reason for my jump from marketing to analytics come from a place of discomfort. I knew we had these amazing tools, and I somewhat knew what they did — but I didn’t know how to use them to do great marketing.</p><p>I would have questions, such as how can I find a lookalike audience for my best customer, and what are the marketing touch points that prompt them to start a trial? Or, are there cohorts of customers that are more likely to convert after they started a trial, and what actions did they take while in trial? My head was spinning with questions, and I knew the answers were there, but they seemed inaccessible to me. It was a very discomforting position to be in.</p><p>I wanted to be the new breed of marketer who combines a deep understanding of his customers and business model with an analytical skillset. I made my ambition known to my friends, mentors, and managers at the company. I actively sought out projects that involved analytics or working with software tools I wasn’t familiar. Through these projects, I realized I enjoyed analyzing data and uncovering insights more than other pieces of marketing.</p><p>Analytics was calling me, so I scheduled a meeting with my manager to drop the bomb that that I wanted to switch teams. I was nervous, in my head, I repeatedly went over the checklist of things I wanted to say. Justify the switch by linking marketing to analytics, check. Reference past projects and experiences that have prepared me for this roll, okay got it. Don’t forget what you’re supposed to say!</p><p>Lucky for me Envoy is still small, and I get to do a bit of everything. The conversation went well, and I was able to switch from Marketing to Data Science. I’ve been in my new role for a month and a half, and have learned several important things about the role of a data analyst:</p><h4><strong>We are partners with internal stakeholders</strong></h4><p>People from different parts of the organization from Revenue, Success, Product, Marketing up to the CEO all have questions, gut feelings, and instincts about their work. Our role as analysts involves providing them with the answers to these questions and giving them access to the knowledge they need to build a successful company.</p><h4><strong>Get over feeling that you’re “not technical”</strong></h4><p>I have a finance background, which means I have a lot to learn about data analytics. One management concept I came across in college that stuck with me is the importance of <a href="http://study.com/academy/lesson/self-efficacy-self-monitoring-in-organizational-behavior.html">high self-efficacy</a>. The basic idea is that if you believe you can succeed in a challenging new situation, then you are more likely to succeed.</p><p>Whenever I encounter a concept I don’t know, I ask questions, I do research and I always believe that I will be able to eventually understand it. Since joining the analytics team I’ve analyzed time series data from multiple data sets to help answer business-critical questions like how well our salesforce is performing, what is responsible for fluctuations in MRR and what are the funnel metrics for our new product. These are all questions I wouldn’t have been able to answer a month ago.</p><h4><strong>Ask questions</strong></h4><p>Your team is there to help you. We all have the same goal, to provide best in class analytics for Envoy. Helping each other makes our team stronger and makes it easier for us to achieve our goals. Don’t be afraid to ask questions, it can literally save you hours of work, what seems difficult for you may be easy for someone else and in most cases they will be happy to lend a hand.</p><h4><strong>Insights must bring about change</strong></h4><p>Insights form data are only valuable if they can bring about organizational changes that help us meet our business objectives. My journey as a marketer and a business leader is not over. I think it’s important to keep on learning, not just about data analytics, but about other departments as well. If there’s a problem that needs solving at Envoy I should be able to use all the tools in my arsenal, from marketing, analytics and elsewhere to solve it. After all, working at a small startup we are expected to be able to do a little bit of everything.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ca18165a86a1" width="1" height="1" alt=""><hr><p><a href="https://envoy.engineering/switching-from-marketing-to-analytics-ca18165a86a1">Switching from marketing to analytics</a> was originally published in <a href="https://envoy.engineering">Envoy Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Sampling for NPS surveys]]></title>
            <link>https://envoy.engineering/sampling-for-nps-surveys-1def21ba5e6b?source=rss----d8aa51f79fb---4</link>
            <guid isPermaLink="false">https://medium.com/p/1def21ba5e6b</guid>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[nps]]></category>
            <category><![CDATA[r]]></category>
            <dc:creator><![CDATA[Kai Chan]]></dc:creator>
            <pubDate>Tue, 21 Nov 2017 19:02:11 GMT</pubDate>
            <atom:updated>2017-11-22T01:00:31.875Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nDqnBC2OD9KQFOAuR8NRNQ.png" /></figure><p>There’s been burgeoning adoption of Net Promoter Score (NPS) as a measure of customer satisfaction and a predictor of growth. NPS excels as an industry benchmark because it is, in majority cases, determined by asking a single survey question:</p><blockquote>How likely is it that you would recommend our company/brand/product to a friend or colleague?</blockquote><p>Here’s how it works: respondents to NPS surveys answer on scale of 0 to 10. Score a 9 or 10 , you’re a Promoter. 7 or 8, you’re a Passive. And anything lower, that makes you a Detractor.</p><p>Promoters get assigned a +1, Passives a 0 and Detractors a -1. This figure is averaged out and multiplied by 100 to give an NPS, which can fall anywhere between -100 (worst) and 100 (best).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WbFgRwHjUp4Ud9i0NSeI3w.png" /><figcaption>NPS scale</figcaption></figure><p>Many of the world’s top companies regularly conduct NPS surveys and benchmark themselves against their industry average, which runs a wide gamut from <a href="https://npsbenchmarks.com/companies">31 for telecommunications to 74 for healthcare</a>. <a href="https://npsbenchmarks.com/companies/tesla_motors">Tesla</a>, for example, is doing great at 97, while <a href="https://npsbenchmarks.com/companies/pacific-gas-and-electric-corp">Pacific Gas and Electric Corp</a> not so much at -6.</p><h4>The sampling problem</h4><p>At Envoy, we run NPS surveys daily, both in-app and through email. In particular, we care about 30-day NPS — calculated by taking the mean NPS over the last 30 days.</p><p>Because we don’t want to spam our customers with surveys all the time, we throttle survey-sending to once every 90 days. This immediately presents a sampling problem: How can we determine the number of customers to survey each day, if we want this number <em>x</em> to draw a smooth curve over time?</p><p>For starters, we obviously can’t survey 100% of eligible customers at the start of every quarter, because then we’d have no respondants in the second and third months and our 30-day NPS would be irrelevant during those periods.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7l3_sVWVQCxSSj845hOsfw.jpeg" /><figcaption>Sampling 100% of customers results in months with no data</figcaption></figure><p>Intuitively, we ought to sample 1/90 of our customer base each day to send surveys out to. This is easier said than done. An approach we employed was to pull lists of eligible customers each day, pick 1.11% of them at random, and send them surveys.</p><p>This worked great on the first day.</p><p>On the second day, what needs to happen is: people surveyed on the first day have to be taken out of the pool. New eligible customers from the day before have to be added to the pool. When you include such complexities as only sending surveys on weekdays during work hours, and taking into account global timezones, this gets messy real fast.</p><p>On day 91, we have to do all that, plus add the people that were surveyed on day 1 back into the pool before sampling. This also means that 90 days is the <em>least</em> amount of time between surveys, when what we really want is for 90 days to be the <em>exact</em> amount of time between surveys.</p><pre># Sample data frame<br>set.seed = 66<br>df_sample &lt;- df[sample(nrow(df), nrow(df)/90), ]</pre><p>Looking back, this was a terrible idea.</p><h4>Smarter sampling</h4><p>After some iterating, it turns out that the better solution is simply to assign each customer a random number between 1 and 90. This way, day 1’s cohort would comprise all the 1’s, day 2’s all the 2’s, and day 91’s all the 1’s again. We can write it this way:</p><pre># Cohort eligible customers<br>set.seed = 66<br>df &lt;- mutate(df, cohort = sample(1:90, nrow(df), replace = TRUE))</pre><p>Then, pick an arbitrary start date and determine today’s cohort:</p><pre># Determine current cohort<br>freq &lt;- 90<br>start &lt;- as.Date(&quot;1970-01-01&quot;)<br>cohort &lt;- as.integer(Sys.Date() - start) %% freq + 1</pre><p>Finally, put it all together by identifying the correct cohort to survey:</p><pre># Sample data frame<br>df_sample &lt;- filter(df, cohort == cohort)</pre><p>Remember how we were talking about drawing a smooth curve of customers surveyed over time? If you model out what we’ve put together, you’ll find a curve with decreasingly small steps around day 91 and every 90 days thereafter. The slope of the curve also closely mimics that of your customer growth, which is exactly what we’re looking for.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*h66iMPBwAKUzwTumghKJSw.jpeg" /><figcaption>Model for surveys sent over time</figcaption></figure><h4>Why sample manually?</h4><p>Several NPS survey providers (<a href="https://www.wootric.com">Wootric</a> and <a href="https://delighted.com">Delighted</a>, to name a couple we’ve used) offer solutions to this very problem. They do a great job throttling surveys — all you really need to do is upload a CSV file of your customers, and they’ll take care of the rest.</p><p>So why would you roll your own sampling solution?</p><p>The simple answer is control. At Envoy, we always want to provide the best possibly customer experience, so our criteria for selecting customers to survey includes:</p><ol><li>Have been on the platform for at least a month;</li><li>Have used the product at least once over the last 7 days;</li><li>Can only receive surveys between 8:00 AM and 5:00 PM on weekdays;</li><li>Belong to a particular set of users defined by permission level;</li><li>Work at companies that are active paying customers.</li></ol><p>What we’ve discovered is that putting in more thought into your sample results in a more accurate and authentic NPS. This process, which we run on <a href="https://www.heroku.com">Heroku</a> off an <a href="https://github.com/virtualstaticvoid/heroku-buildpack-r">R buildpack</a>, helps us to continue building great products around customer satisfaction and feedback.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1def21ba5e6b" width="1" height="1" alt=""><hr><p><a href="https://envoy.engineering/sampling-for-nps-surveys-1def21ba5e6b">Sampling for NPS surveys</a> was originally published in <a href="https://envoy.engineering">Envoy Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Automated Ember deployments]]></title>
            <link>https://envoy.engineering/automated-ember-deployments-492495f95dcd?source=rss----d8aa51f79fb---4</link>
            <guid isPermaLink="false">https://medium.com/p/492495f95dcd</guid>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[ember]]></category>
            <category><![CDATA[ember-cli]]></category>
            <category><![CDATA[emberjs]]></category>
            <dc:creator><![CDATA[Adolfo Builes]]></dc:creator>
            <pubDate>Thu, 25 Aug 2016 22:57:32 GMT</pubDate>
            <atom:updated>2017-11-21T19:13:30.159Z</atom:updated>
            <content:encoded><![CDATA[<p>At Envoy, our deployments are fully automated. In this post, we’d like to show you how we integrated a “lighting fast deployment” strategy using tools like GitHub, Travis, Slack and Dockbit.</p><p>We host our assets in S3. Before Ember CLI Deploy, the easiest way to upload assets would have probably been to use a custom grunt task, which getting to work could easily turn into several hours of hitting your head against a wall.</p><p>Today, the same process can be achieved by running <em>ember install ember-cli-deploy-s3 </em>and then configuring the deploy file with your AWS credentials.</p><p>We are using S3 to serve our <em>index.html </em>instead of Redis. To do so, we are using <a href="https://github.com/ember-cli-deploy/ember-cli-deploy-s3-index">ember-cli-deploy-s3-index</a> which also allows us to upload different revisions before making it available to our users.</p><p>Our config file looks like the following:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/62d0451a008dff9a753ca366fffb33a5/href">https://medium.com/media/62d0451a008dff9a753ca366fffb33a5/href</a></iframe><p>With both plugins configured, deploying our dashboard is a matter of running <em>ember deploy staging</em> or <em>ember deploy production</em>. Then we can activate a revision with <em>ember deploy:activate production -r revision.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KWF3-_yAhrPwX98Dc_ptlA.png" /><figcaption>Uploading a new revision to staging</figcaption></figure><p>If we are not sure about which revision to activate, we can look at the list running <em>ember deploy:list staging</em>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sduIiH5kKZapKkHxJnADyw.png" /><figcaption>Listing available revisions in staging, the second from top to bottom is the currently activate.</figcaption></figure><h4>Automate the mundane</h4><p>After getting Ember CLI Deploy to work, something didn’t feel quite right yet. We were still doing some manual tasks like running the deploy command to ship PRs to staging or even uploading and activating the latest commit in production.</p><p>One of Envoy’s core <a href="https://envoy.com/about/">values</a> is <em>automate the mundane</em>, and honoring that value we decided to go deeper and simplify further our deployment process.</p><p>Our requirements were:</p><ol><li>Deploy automatically to staging every PR that passes all the tests.</li><li>Deploy every merge into master to staging and production.</li><li>Attach a notification to the PR in GitHub so we can test easily in staging.</li><li>Make master the default index in staging.</li><li>Notify us in slack that a new revision is ready to be activated in production.</li></ol><h4>Travis</h4><p>To deploy automatically to staging we created an <em>after_sucess</em> hook in Travis that calls a custom script after the tests have passed. Our script is called <em>travis_deploy.sh</em> and it has the following content:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/50fae9382e2ced34a8d269c5f45d2631/href">https://medium.com/media/50fae9382e2ced34a8d269c5f45d2631/href</a></iframe><p>The previous script always deploys the commit to staging, and if we are on “master” then it also deploys to production. Great! One chore less to run, but we were still missing steps 3 to 5: the PR notifications and slack message.</p><h4>GitHub</h4><p>GitHub’s API allow us to create <a href="https://developer.github.com/v3/activity/events/types/#deploymentevent">deployment events</a> and attach them to different PRs. We use that to attach a notification when the deployment starts and then include a link to the revision in staging once it has been deploy.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TLpYhgT24lFfRtV9iYxuxg.png" /><figcaption>Clicking on “deployed” will take us to staging.server.com/index.html:bb4a31b</figcaption></figure><p>We use the following script called “deploy-to-staging.rb” to generate the notification above:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9c0f657fd5510f43be9aaf0b4ccef9e1/href">https://medium.com/media/9c0f657fd5510f43be9aaf0b4ccef9e1/href</a></iframe><p>And then, we edited our deployment script, so the script above is called only if the current branch is not master. Also, we automatically activate staging if the branch is master:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/04d27d30e06a095b5f349ededef51176/href">https://medium.com/media/04d27d30e06a095b5f349ededef51176/href</a></iframe><p>Once we solved requirements 3 and 4 the only thing we had left was to add slack notifications.</p><h4>Slack</h4><p>To get a notification in slack we added an <a href="https://api.slack.com/incoming-webhooks">incoming webhook</a> and then put the following after activating staging:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/92b533ae4ffc80a951fc8185552007bf/href">https://medium.com/media/92b533ae4ffc80a951fc8185552007bf/href</a></iframe><p>Every time master is deployed to production (remember we don’t activate by default), we get a message like the following:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QV9HZuXo78c7EQa6_oqQSA.png" /></figure><p>As easy like that, we got our fifth requirement.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/245/1*U0Jplbr_4TNkqMafPqhVmA.gif" /></figure><p>Everything was happiness and all that jazz. But, we were still running a manual command to activate the latest revision in master. Using as example the <em>revision</em> above, one of our engineers would have to go to their terminal an run something like <em>ember deploy:activate production -r 926e624</em>, which turned into a boring task.</p><h4>Dockbit</h4><p>We started to use <a href="https://dockbit.com/">Dockbit</a> to automate our API deployments through Slack, and we decided to do the same for our dashboard.</p><p>After adding the repository, we created a new pipeline with a single step doing the following:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/42d770f02365e1bc3e0919b40d634708/href">https://medium.com/media/42d770f02365e1bc3e0919b40d634708/href</a></iframe><p>Dockbit was the missing piece in our workflow, and it help us to automate our deployment process. Now, anyone can activate running a single command in Slack. We edited our “after_success” script to include the activation command, making it easier for us to activate:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uwh9ht0_gy3hLDCgm3sYGA.png" /></figure><h4>Wrapping up</h4><p>The term “lighting fast deployment” was probably brought to the Ember community after Luke Melia’s talk at RubyConf 2014: <a href="https://www.youtube.com/watch?v=QZVYP3cPcWQ">Lightning Fast Deployment of Your Rails-backed JavaScript app</a>.</p><p>Two years after that, the same process can be replicated quite easily thanks to the tools mentioned above.</p><p><a href="http://ember-cli-deploy.com/">Ember CLI Deploy</a> makes a breeze the assets handling part. Travis, GitHub and Slack take care of uploading and notifications, and Dockbit allows everyone on the team to finally ship code to production.</p><p>Do you have a different deployment workflow for your Ember apps? We would love to hear about it! At Envoy we love learning from others and we’re always looking for new ways to improve our processes.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=492495f95dcd" width="1" height="1" alt=""><hr><p><a href="https://envoy.engineering/automated-ember-deployments-492495f95dcd">Automated Ember deployments</a> was originally published in <a href="https://envoy.engineering">Envoy Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The secret sauce of Envoy Passport stamp images]]></title>
            <link>https://envoy.engineering/the-secret-sauce-of-envoy-passport-stamp-images-cfac8d4bdbb1?source=rss----d8aa51f79fb---4</link>
            <guid isPermaLink="false">https://medium.com/p/cfac8d4bdbb1</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[design]]></category>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[image-processing]]></category>
            <category><![CDATA[technology]]></category>
            <dc:creator><![CDATA[Fang-Pen Lin]]></dc:creator>
            <pubDate>Thu, 07 Jul 2016 21:14:37 GMT</pubDate>
            <atom:updated>2017-11-27T21:50:45.422Z</atom:updated>
            <content:encoded><![CDATA[<p>If you use <a href="https://envoy.com/passport/">Envoy Passport</a>, you might already know that we recently announced an <a href="https://blog.envoy.com/stamp-collectors-rejoice-d34aac0a17c1">awesome feature</a> that allows you to share stamps of the places you’ve visited on your social networks.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/770/1*mOila160EMatmQIvnmmP9w.gif" /><figcaption>Real-time visitor sign-in dashboard at Envoy HQ</figcaption></figure><p>Question: have you ever wondered how those beautiful stamps are made?</p><p>The truth is, unfortunately, rather brutal: to make beautiful stamp images for the thousands of companies who use Envoy, we put our poor designers (Amy Devereux, <a href="https://medium.com/u/ef21bfddd89c">Sze Wa Cheung</a>, <a href="https://medium.com/u/83c230c72f4e">Wells Riley</a> and <a href="https://medium.com/u/51d2eca6b872">Jon Rundle</a>) on call 24 hours a day, 7 days a week. Their task? Manually create stamp images with Photoshop whenever a new customer signs up.</p><p>Just kidding! That’s not how things are done in Envoy (phew!). At Envoy, one of our core values is:</p><blockquote>Automate the mundane</blockquote><p>As such, we actually built software for making stamp images. Even though it might just seem like an image you show on your phone, we spent a lot of time and effort to make sure it looks perfect.</p><h3>Why creating stamps is difficult</h3><p>At first glance, turning a company logo into a stamp image is not that hard. With basic image processing knowledge, you simply convert the company logo as a monotone color, then draw it as a mask with the theme color on the canvas. Voila! Done.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GnPOic0pyBONUj09Xnb97Q.png" /><figcaption>Original logo images</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5COkgddElSZuZds6mmcvew.png" /><figcaption>Stamp images converted from logo images</figcaption></figure><p>This actually works fine for most company logos. However, the world is more colorful than you may expect. Most companies use only a single color in their company logo, but there are still many which do not.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tWWs2xN4FGzRVzmfEGdC3g.png" /><figcaption>Original colorful logo images</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DIdwwGVD2q7lkjNLcwAVvg.png" /><figcaption>Stamp images converted from colorful logo images</figcaption></figure><p>Oops! As you can see, the problem is that there are components with different colors in the logo. When they are converted into monotone colors, there is no way the distinguish the boundaries originally defined by differing colors.</p><h3>The solution: look for the edges</h3><p>To address the problem, a straightforward approach is to stroke the edge between color components. But here’s another problem: how can we know where the edge is? For example, given a real-life photo like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*FwRCG7C7wi31yA8NwoRzjw.png" /><figcaption>Picture from Wikipedia <a href="https://commons.wikimedia.org/wiki/File:Valve_original_(1).PNG">https://commons.wikimedia.org/wiki/File:Valve_original_(1).PNG</a>, CC-BY-SA-3.0</figcaption></figure><p>…how can you find the edges? By definition, it’s the pixels where the difference between neighborhood pixels is huge.</p><p>There is actually a well-developed image processing technique called <a href="https://en.wikipedia.org/wiki/Edge_detection">edge detection</a> that can be used to solve this problem. We tried different edge detection algorithms, and realized that a <a href="https://en.wikipedia.org/wiki/Sobel_operator">Sobel filter</a> provides the best result.</p><p>The fundamental idea of a Sobel filter for edge finding is this: there are two filter matrixes to be applied on the picture pixels:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/538/1*hn0PzaGYDR3ChI9dlxNJTA.png" /></figure><p>These two matrices subtract nearby pixel values from horizontal and vertical directions to, essentially, produce the difference of nearby pixels. By combining the signal strength of nearby pixel difference</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/170/1*VNhSDrJEQ7t7nKRvxn9WXw.png" /></figure><p>We get a edge stroked image:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/640/1*i-2pxi6vf1jgjOUtMeQrSA.png" /><figcaption>Sobel image result from Wikipedia <a href="https://commons.wikimedia.org/wiki/File:Valve_sobel_(3).PNG">https://commons.wikimedia.org/wiki/File:Valve_sobel_(3).PNG</a>, CC-BY-SA-3.0</figcaption></figure><p>With the edge image, we can finally make the edges of colorful company logos distinguishable in stamp images.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gzODvFsFxFKR-YZNlhhreQ.png" /><figcaption>Logo image stoked with edge image generated from Sobel filter</figcaption></figure><h3>Let’s make it even better</h3><p>The story doesn’t end here. Although the non-distinguishable edge in stamps is now distinguishable, we noticed there was a drawback to this approach.</p><p>Since the original distinguishable edge, like the outer edge between the visible and invisible area, is also stroked, it actually slightly shrinks the logo image.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/370/1*7xcEvs9qFPaNX-7dtN77Og.png" /><figcaption>Logo image edge shrinks a little bit due to edge stoking, the blue color on edge indicates the shrinked area</figcaption></figure><p>Even though shrinking of the logo size on the edge is barely noticeable by the human eye, we wanted to make the stamp image as perfect as possible. To achieve that, we came up with an idea: the alpha channel also has an edge.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/359/1*WUR0Iiplp1YdzCWO-TrfqA.png" /><figcaption>Alpha channel of logo image</figcaption></figure><p>We can also get the edge for it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/331/1*CuDLpd1q6q4xg7yMbMVWBw.png" /><figcaption>Sobel image of alpha channel</figcaption></figure><p>As we only wanted to stroke the real non-distinguishable edges, we used the edge of the image of the RGB channel to subtract the the image of Alpha channel. Here are the real non-distinguishable edges:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GP5wOYy0HM1OvAw_cOGa_Q.png" /><figcaption>RGB channel Sobel image subtracts Alpha channel Sobel image</figcaption></figure><p>Finally, we stroked the real non-distinguishable edges on the stamp and made the perfect stamp image.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/341/1*-hK5Sg6I0ix3-_XbPSRwQw.png" /><figcaption>Logo image stored with non-distinguishable edge image as mask</figcaption></figure><p>The result looks great (if we do say so ourselves.)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jZja7cm9_Dxcw36n-AI1WQ.png" /></figure><h3>Envoy is hiring</h3><p>Admittedly, we’re a bit crazy. If you’re as crazy about pursuing quality and building the best product in the world, join us. As an exponentially growing startup, the work you craft will directly impact millions of people around the world. Here are the open positions:</p><p><a href="https://jobs.lever.co/envoy/0b08f513-c4fc-486a-a5e5-d9859e2f5a1d?lever-source=engineering-blog-passport-stamp">iOS Product Engineer</a></p><p><a href="https://jobs.lever.co/envoy/34d9a082-f5a0-4695-92c3-e7657b0b3164?lever-source=engineering-blog-passport-stamp">Ruby Backend Engineer</a></p><p>Shoot us your resume if you’re interested!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cfac8d4bdbb1" width="1" height="1" alt=""><hr><p><a href="https://envoy.engineering/the-secret-sauce-of-envoy-passport-stamp-images-cfac8d4bdbb1">The secret sauce of Envoy Passport stamp images</a> was originally published in <a href="https://envoy.engineering">Envoy Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Embedded web server for iOS UI testing]]></title>
            <link>https://envoy.engineering/embedded-web-server-for-ios-ui-testing-8ff3cef513df?source=rss----d8aa51f79fb---4</link>
            <guid isPermaLink="false">https://medium.com/p/8ff3cef513df</guid>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[testing]]></category>
            <dc:creator><![CDATA[Fang-Pen Lin]]></dc:creator>
            <pubDate>Tue, 28 Jun 2016 19:24:49 GMT</pubDate>
            <atom:updated>2017-11-21T19:12:13.153Z</atom:updated>
            <content:encoded><![CDATA[<p>If you’ve ever visited some of the tech companies around Silicon Valley, you may have seen an iPad stand at the front desk with something like this on the screen:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/884/1*aAanAf4NCPRAzyqx10HidA.png" /></figure><p>Look familiar? That’s <a href="http://envoy.com">Envoy</a>, our visitor registration app, which has signed in more than 5 million visitors in three years at companies all around the world.</p><p>Recently, we sometimes found ourselves trapped in a loop of fixing bugs instead of creating new features for the iPad app. The fundamental problem here is that we’ve incurred <a href="http://victorlin.me/posts/2013/10/07/technical-debts">too much technical debt.</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/442/1*ZCcpqwZUIWMxgtTw9K_UbQ.png" /></figure><p>Software development is hard. To deliver the highest quality software in the industry and continually evolve, we need to pay back the technical debt we incurred by adopting automatic tests.</p><h3>Mocking is hard</h3><p>To write unit tests for the legacy code base, we need to refactor the design objects and architecture to make them testable first. However, without existing tests to ensure all functionalities are still working as expected, it’s difficult to refactor. As it turns out, it’s a chicken or egg problem.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/648/1*5xdjs3a7UzrA3FzPApn4Kg.png" /></figure><p>Fortunately, in addition to writing unit tests against classes, we can write tests for UI interaction against the whole app. As long as we have good coverage from the UI surface down to the app, we can then refactor the legacy code inside the app later without worrying too much about breaking it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*Kb6vZQsVFnwpU7dmOkkeXg.gif" /><figcaption>Screencast for UI automatic testing</figcaption></figure><p>Although UI testing solves the chicken or egg problem of testing and refactoring, it’s not an easy thing to do. The major issue we’ve encountered is that due to the way UI testing works, there are two standalone processes: 1) the UI testing runner and 2) the target app. The UI testing runner launches the target app and uses the accessibility infrastructure to inspect and control the app.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/648/1*Xy7gDCRGdWAu1V2Mk9ABSQ.png" /></figure><p>To test our app, we need to control the data returned from the API client object, but as they are two standalone processes, you cannot control in-memory values directly from one process to another. You’re unable to do this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/935648a6eae671dcdbe41beee9c84603/href">https://medium.com/media/935648a6eae671dcdbe41beee9c84603/href</a></iframe><p>Then replace the API and set the mock data to return:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0fb08419f4edcdc6b87e5722b152ad33/href">https://medium.com/media/0fb08419f4edcdc6b87e5722b152ad33/href</a></iframe><h3>Iteration #1: Pass environment variables</h3><p>Despite that you cannot mock the API directly (like what you do for unit tests), you can pass <a href="https://en.wikipedia.org/wiki/Environment_variable">environment variables</a> and arguments to the target app when you launch it like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/fc19fd682cccab02d5b7d3cf41f4e563/href">https://medium.com/media/fc19fd682cccab02d5b7d3cf41f4e563/href</a></iframe><p>With that in mind, our first approach was to write a mock API client that mocks API calls according to environment variables.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e6ce959ff4e69b76f0fb3823c6680bd2/href">https://medium.com/media/e6ce959ff4e69b76f0fb3823c6680bd2/href</a></iframe><p>Passing environment variables and mocking the API function is easy and works, but there are many drawbacks to this approach:</p><ul><li>Need to write code for reading environment variables and mocking the API</li><li>Hard to control the API, like delaying the response or no response to test timeout handing</li><li>API behavior is not customizable. For example, if we want to return different values according to the received parameters</li><li>Hard to check API calls and the data passed in</li></ul><h3>Iteration #2: Embassy and Ambassador</h3><p>After writing a few test cases with the environment variables approach, we realized that it’s obviously not the best way to write tests. In the end, what we mocked is an APIClient that connects to the HTTP server, but then we thought: why not just mock the HTTP API server instead?</p><p>We looked around to find open source HTTP servers written in Swift, but since Swift is a relatively new programming language and Apple just <a href="https://swift.org">open sourced</a> it a while ago, the resources for server-side Swift were very limited. We did find <a href="https://github.com/httpswift/swifter">swifter</a>, however, it’s a sync style server and we want async. We also found <a href="https://github.com/PerfectlySoft/Perfect">Perfect</a> and <a href="https://github.com/IBM-Swift/Kitura">Kitura</a>, but they‘re both geared towards being the production ready web application solution in Swift, which is too much heavy weaponry for us.</p><p>You may ask why not use existing resource from other language community, certainly we can, but as if it’s not in Swift, it brings extra burden for our iOS engineers to context switch between different languages. And there are things you cannot do or hard to do, like in-line assertion in the API response handler just right inside the test case.</p><p>Since we couldn’t find anything that met our needs, we finally decided to build the wheel ourselves. It’s called <a href="https://github.com/envoy/Embassy">Embassy</a>, a super lightweight async HTTP server built purely in Swift — and of course, it’s open source.</p><p>Some awesome features:</p><ul><li>Super lightweight, only 1.5 K lines</li><li>Zero third-party dependency</li><li>Async event loop-based HTTP server, makes long-polling, delay and bandwidth throttling all possible</li><li>HTTP Application based on <a href="https://github.com/envoy/Embassy#whats-swsgi-swift-web-server-gateway-interface">SWSGI</a>, super flexible</li><li>IPV6 ready, also supports IPV4 (dual stack)</li></ul><p>For this HTTP server, we defined a gateway interface called SWSGI, a hat tip to Python’s <a href="https://www.python.org/dev/peps/pep-3333/">WSGI (Web Server Gateway Interface)</a>. It’s basically a simple function defined as:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5f5dd3dfbc914cb76ea3c2bbef6a889a/href">https://medium.com/media/5f5dd3dfbc914cb76ea3c2bbef6a889a/href</a></iframe><p>It decouples web applications from the implementation details of the web server and also allows middleware to be wrapped around other web applications. It’s very easy to set up an HTTP server and run it inside your UI tests. Dealing with SWSGI is also easy, but to make mocking APIs even easier, we built <a href="https://github.com/envoy/Ambassador">Ambassador</a>, a lightweight web framework based on SWSGI.</p><h3>Automatic UI testing in action</h3><p>With Embassy and Ambassador, mocking APIs and writing UI tests couldn’t be easier. You can install them with <a href="https://cocoapods.org">CocoaPods</a>: add Embassy and Ambassador to your Podfile target to the UI test like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/daa1c0de1c8405406b2802bc992d17e6/href">https://medium.com/media/daa1c0de1c8405406b2802bc992d17e6/href</a></iframe><p>and run “pod install.”</p><p>Here’s an example how you run a simple HTTP API server:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/89bed833994236a7601875916561bcfb/href">https://medium.com/media/89bed833994236a7601875916561bcfb/href</a></iframe><p>Next, configure your app to connect to “http://localhost:8080/api/v2/users” instead of the real API server. It should be able to get the provisioned JSON payload, which looks like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/106e44491db208d5c496bd86fd30d32a/href">https://medium.com/media/106e44491db208d5c496bd86fd30d32a/href</a></iframe><p>To avoid writing the handlers for the same endpoints again and again, we provide a <strong>DefaultRouter</strong> that has decent default handler build-in, like so:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4dbaddac6ae81ac0f9f9b3e4c7d3f38c/href">https://medium.com/media/4dbaddac6ae81ac0f9f9b3e4c7d3f38c/href</a></iframe><p>Then, we have a base class <strong>UITestBase</strong> for all UI tests cases to inherit:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ae9c1fda6a32dfc83e2c3294ee7f4a06/href">https://medium.com/media/ae9c1fda6a32dfc83e2c3294ee7f4a06/href</a></iframe><p>As you can see, we pass <strong>ENVOY_BASEURL</strong> here as the API base URL for your own API client. You also need to make it read from the environment variable.</p><p>Finally, you can now write test cases with mocked APIs:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/723bf9fbcc3821af2484fc881cf90bfb/href">https://medium.com/media/723bf9fbcc3821af2484fc881cf90bfb/href</a></iframe><p>You simply overwrite the endpoint, you can assert and whatever you want in the response handler.</p><h3>More than just mocking APIs</h3><p>One good thing about bringing embedded web servers inside UI tests is that you can also mock other services, like <a href="https://www.pubnub.com">PubNub</a>, we use it to allow server to notify the iPad app for certain events. We use a classic HTTP long polling technique introduced in the Ajax/Comet era to allow the test runner to push messages to the target app in real-time. This way, the test cases can push a message to the app whenever they want.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qdfP4wa8Zd4mj3ZwHQZ3cA.png" /><figcaption>UI Testing architecture diagram</figcaption></figure><p>As the iPad app supports <a href="https://envoy.com/custom-badges/">badge printing</a>, we also created a protocol for badge printer and created a mock client connecting to an HTTP API endpoint for printer instead of real printer. This means we can examine the printed document from the iPad.</p><h3>Wanna build cool stuff with us? Envoy is hiring</h3><p>As a language, Swift was introduced mainly for building iOS apps and desktop apps. Currently, there are limited resources available, but we see huge potential in bringing server side technology into the Swift community.</p><p>The SWSGI gateway interface we defined for Embassy demonstrates how the server side ecosystem can be built on top of it. What’s great is that this was originally a 20% time project and meant for UI testing purposes only.</p><p>These are just a few of the cool things we’ve built at Envoy. If you also enjoy building cool stuff and solving hard problems, Envoy is hiring. Here are the open engineering positions:</p><p><a href="https://jobs.lever.co/envoy/0b08f513-c4fc-486a-a5e5-d9859e2f5a1d?lever-source=engineering-blog-ios-testing-with-embassy">iOS Product Engineer</a></p><p><a href="https://jobs.lever.co/envoy/34d9a082-f5a0-4695-92c3-e7657b0b3164?lever-source=engineering-blog-ios-testing-with-embassy">Ruby Backend Engineer</a></p><p>Feel free to shoot us your resume if you’re interested. Thanks! :)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8ff3cef513df" width="1" height="1" alt=""><hr><p><a href="https://envoy.engineering/embedded-web-server-for-ios-ui-testing-8ff3cef513df">Embedded web server for iOS UI testing</a> was originally published in <a href="https://envoy.engineering">Envoy Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>