<?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[Stories by Pepijn Senders on Medium]]></title>
        <description><![CDATA[Stories by Pepijn Senders on Medium]]></description>
        <link>https://medium.com/@pepijns?source=rss-7448f8460d7a------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*BguqjG4XSiKVpqlh.jpg</url>
            <title>Stories by Pepijn Senders on Medium</title>
            <link>https://medium.com/@pepijns?source=rss-7448f8460d7a------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 15:15:50 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@pepijns/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[Leveraging Web Workers for performance at HelloFresh — Part 1]]></title>
            <link>https://engineering.hellofresh.com/leveraging-web-workers-for-performance-at-hellofresh-part-1-c12865b443bf?source=rss-7448f8460d7a------2</link>
            <guid isPermaLink="false">https://medium.com/p/c12865b443bf</guid>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[web-workers-in-javascript]]></category>
            <category><![CDATA[opentelemetry]]></category>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[observability]]></category>
            <dc:creator><![CDATA[Pepijn Senders]]></dc:creator>
            <pubDate>Fri, 05 May 2023 15:13:03 GMT</pubDate>
            <atom:updated>2023-05-05T15:13:03.632Z</atom:updated>
            <content:encoded><![CDATA[<h3>Leveraging Web Workers for performance at HelloFresh — Part 1</h3><p>At HelloFresh (and like many other companies) we run a lot of different tasks on the front-end, and if we run them on the front-end we run them on the main thread. Running any task on the main thread will come at a cost, and you pay that cost for how quickly your app <a href="https://react.dev/reference/react-dom/client/hydrateRoot#hydrating-server-rendered-html">hydrates</a>.</p><blockquote>Front-end applications need to “hydrate” when you pre-render the HTML on the server. If you use next.js your app will do this out of the box for you. Hydration happens when the browser reads the HTML, loads the JavaScript, and the JavaScript needs to “attach” itself to the HTML to make it dynamic. Making this faster will improve almost all your core metrics.</blockquote><p>Some of the tasks that <em>we</em> run on the main thread for initial hydration are:</p><ul><li>Providing context values to our entire application</li><li>Bootstrapping third-party tooling (like AB-testing tooling)</li><li>Bootstrapping metrics solutions</li><li>Hydrating fetched server-side data</li><li>Initializing legacy redux stores</li><li>Hydrating all the visual components (the header, the content, the footer, etc.)</li></ul><p>This list might not sound like much, but it can quickly add up. Our average <a href="https://nextjs.org/docs/app/building-your-application/optimizing/analytics#custom-metrics">hydration time</a> before adding Web Workers improvements was 450ms!</p><p>If you profile your page, you can bucket the tasks executed into two buckets; hydrating components, and other tasks (like providing context and any other tooling initialization).</p><p>For us, this differs per page but it’s usually 60% time spent on hydrating the visual components of the page, and 40% on running the other tasks. If the page has more components usually it skews more in the direction of the hydrating of visual components.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JNhZt4C2w0CwBpfAHgAk1Q.png" /></figure><p>Optimizing the other tasks could give us a potential improvement of 200ms (40%) in our <a href="https://nextjs.org/docs/app/building-your-application/optimizing/analytics#custom-metrics">hydration time</a>. So it’s a no-brainer to try to fix this!</p><blockquote>This is excluding any improvement we would potentially make on further render cycles. As some of these context providers have active communication with the rest of the app during the entire lifecycle of a user’s session.</blockquote><h3>Moving tasks to another thread — the Web Worker</h3><p>There’s only one way to multi-thread on the web, and this is through a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers">Web Worker</a>. It is a new-ish API, but its browser adoption is quite good. According to <a href="https://caniuse.com/webworkers">caniuse</a> 98% of the web’s users have access to this API. The Web Worker API isn’t as easy as doing everything in the same main thread, you will need to adapt your tooling to work with Web Workers.</p><p>To work with Web Workers you need to understand that the Web Worker code runs in a different environment than your main application running on the main thread. You can’t access things like window variables for example. The only way to send data to the web worker is through an event sent through the postMessage function.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FEFan2GNbvSkJaLrYjhT9g.png" /></figure><p>As you maybe already noticed, this could make any synchronous API an asynchronous API, where you’d have to wait for the worker to respond with a message. This is why we split our tasks into another bucket, the send-and-forget tasks (where we don’t care for a response), and the tasks where we depend on the response of the API.</p><h4>Send-and-forget tasks</h4><p>We have a couple of these tasks, and we can also recommend a third-party library called <a href="https://partytown.builder.io/">partytown</a> to offload your scripts to the web worker. It works great!</p><p>One of the tasks we started off with wasn’t compatible with this though, and needed some custom tinkering. We started off by moving our metrics and tracing to a Web Worker, for which we use <a href="https://github.com/open-telemetry/opentelemetry-js">OpenTelemetry</a>. If you’re not familiar with OpenTelemetry, it’s one of the leading APIs for doing any kind of tracing, and they ported their library to many different platforms, including web. We’ve been using it for a while and it has been great. But never mind the tool, we’re moving it to a Web Worker! We found it was accounting for 5–10% of the total <a href="https://nextjs.org/docs/app/building-your-application/optimizing/analytics#custom-metrics">hydration time</a> of our pages on average.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*x2huFTwgYooMp4x1NHjSWw.png" /><figcaption>Screenshot from our profiler (TracerProvider initializes the OpenTelemetry client and propagators)</figcaption></figure><p>A simplified version of the API signature of the OpenTelemetry library looks something like this:</p><pre>const span = tracer.startSpan(<br>  &#39;mytrace&#39;,<br>  {<br>    attributes: {<br>      myAttribute: true<br>    },<br>  }<br>);<br>span.setAttribute(&#39;test&#39;, &#39;test&#39;);<br>span.end();</pre><p>If we’re thinking about send-and-forget APIs this of course looks a bit more complex, as it depends on different stages of sending certain related data points to this API. The API needs to know when the span started, when a new attribute is added, and when it ends.</p><p>We worked around this problem by creating an interface on the main thread, where we give the other thread the ability to match the events of the main thread to the same span. The interface we built looks like this (the API is not the complete OpenTelemetry API, but we ported each function):</p><pre>let spanCounter = 0;<br><br>const startSpan: Tracer[&quot;startSpan&quot;] = (name, options) =&gt; {<br>  const identifier = `${spanCounter}.${name}`;<br>  spanCounter++;<br><br>  eventBus.pushEvent({<br>    name: &quot;startSpan&quot;,<br>    payload: {<br>      identifier,<br>      name,<br>      options<br>    },<br>  });<br><br>  const span: Span = {<br>    name,<br>    setAttribute: (key, value) =&gt; {<br>      eventBus.pushEvent({<br>        name: &quot;setSpanAttributes&quot;,<br>        payload: {<br>          identifier,<br>          attributes: { [key]: value },<br>        },<br>      });<br><br>      return span;<br>    },<br>    setAttributes: (attributes) =&gt; {<br>      eventBus.pushEvent({<br>        name: &quot;setSpanAttributes&quot;,<br>        payload: {<br>          identifier,<br>          attributes,<br>        },<br>      });<br><br>      return span;<br>    },<br>    end: (endTime) =&gt; {<br>      eventBus.pushEvent({<br>        name: &quot;endSpan&quot;,<br>        payload: {<br>          identifier,<br>        },<br>      });<br>    },<br>  };<br><br>  return span;<br>};<br><br>const tracer = {<br>  startSpan,<br>};</pre><p>You can see that we send events on each function call to our event bus provider. This event bus communicates with our Web Worker, and each event has a handler inside the worker. The main events here are: “startSpan”, “setSpanAttributes” (we reuse the same event for setAttribute and setAttributes), and “endSpan”. Each event will have the identifier in its payload and that can then be referenced inside the worker.</p><p>For example the event handler for startSpan looks like this:</p><pre>export const spanCache: Record&lt;string, Span&gt; = {};<br><br>const startSpan = (<br>  tracer: Tracer,<br>  { identifier, name, options }: StartSpanPayload<br>) =&gt; {<br>  spanCache[identifier] = tracer.startSpan(<br>    name,<br>    options<br>  );<br>};</pre><p>And inside our other handlers, we can reference get the current span from the spanCache variable:</p><pre>const endSpan = ({ identifier }: EndSpanPayload) =&gt; {<br>  if (identifier in spanCache) {<br>    spanCache[identifier].end();<br>    delete spanCache[identifier];<br>    return;<br>  } <br><br>  throw new Error(`${identifier} not found, it was probably already ended!`);<br>};<br><br>const setSpanAttributes = ({<br>  identifier,<br>  attributes,<br>}: SetSpanAttributesPayload) =&gt; {<br>  if (identifier in spanCache) {<br>    spanCache[identifier].setAttributes(attributes);<br>    return;<br>  }<br><br>  throw new Error(`${identifier} not found, it was probably already ended!`);<br>};</pre><h4>Maintaining context</h4><p>With tracing you want to maintain the context of the parent spans of your trace. OpenTelemetry needs the parent span object to reference this context. Because we don’t have this span object in our main thread we had to come up with a different API than the original API from OpenTelemetry. This is the <em>only difference </em>with the original API as we tried to keep everything as similar as possible to reduce refactoring efforts and prevent difficult upgrades when the OpenTelemetry API changes.</p><p>The reference to these parent spans only exists within the worker thread, so we needed to design a way to pass this reference from the main thread. Now when someone wants to reference a parent span context they have to write a slightly different function:</p><pre>consconst parentSpan = tracer.startSpan(&quot;parent-span&quot;);<br>const span = tracer.startSpan(<br>  &quot;child-span&quot;,<br>  undefined,<br>  parentSpan.getIdentifier()<br>);</pre><p>This identifier, you probably guessed it, is the string value we create inside the startSpan interface, which we reference inside the event handlers of the web worker. With this in mind our startSpan handler will look like this:</p><pre>const startSpan = (<br>  traceContext: TraceContext,<br>  { identifier, name, options, context: parentSpanIdentifier }: StartSpanPayload<br>) =&gt; {<br>  if (parentSpanIdentifier) {<br>    const contextOfSpan = trace.setSpan(<br>      context.active(),<br>      spanCache[parentSpanIdentifier]<br>    );<br><br>    spanCache[identifier] = traceContext.tracer.startSpan(<br>      name,<br>      options,<br>      contextOfSpan<br>    );<br>  } else {<br>    spanCache[identifier] = traceContext.tracer.startSpan(<br>      name,<br>      options<br>    );<br>  }<br>};</pre><h4>Timing discrepancies</h4><p>When we send events to the worker it could be that there is some delay in when it is handled. According to MDN, this is 0–1ms, but we wanted to prevent polluting our data with potentially inaccurate timing due to eventing delays. This is why we started referencing all our timings inside the main thread, the worker thread was just a consumer of these timings.</p><p>This means that our startTimePayload would have the start time from the main thread; like so:</p><pre>eventBus.pushEvent({<br>  name: &quot;startSpan&quot;,<br>  payload: {<br>    identifier,<br>    name,<br>    options: {<br>      startTime:<br>        typeof options?.startTime === &quot;number&quot;<br>          ? options.startTime<br>          : getCurrentTimestamp(),<br>    },<br>  },<br>});</pre><p>People also have the ability to add a custom start time, so we have to account for this, but the default will be the current timestamp when startSpan is called in the <strong>main</strong> thread. We did the same for the end time of our spans:</p><pre>eventBus.pushEvent({<br>  name: &quot;endSpan&quot;,<br>  payload: {<br>    identifier,<br>    endTime:<br>      typeof endTime === &quot;number&quot; ? endTime : getCurrentTimestamp(),<br>  },<br>});</pre><p>Which had to be handled as well by the event handler:</p><pre>const endSpan = ({ identifier, endTime }: EndSpanPayload) =&gt; {<br>  if (identifier in spanCache) {<br>    spanCache[identifier].end(endTime);<br>    delete spanCache[identifier];<br>  }<br>};</pre><h4>Creating the worker asynchronously</h4><p>We noticed that creating the worker in sync with the rendering of the tree is also not the most performant. Imagine the following two components:</p><pre>const TracerProvider = () =&gt; {<br>  const worker = useMemo&lt;Worker&gt;(<br>    () =&gt; new Worker(new URL(&#39;./worker/index.ts&#39;, import.meta.url))<br>  );<br>);<br><br>const TracerProvider = () =&gt; {<br>  const workerRef = useRef&lt;Worker&gt;();<br>  <br>  useEffect(() =&gt; {<br>    workerRef.current = new Worker(<br>      new URL(&#39;./worker/index.ts&#39;, import.meta.url)<br>    );<br>  }, []);<br>};</pre><p>When we create the worker on an effect (after all the main rendering work is done), it doesn’t impact the <a href="https://nextjs.org/docs/app/building-your-application/optimizing/analytics#custom-metrics">hydration time</a>. The one downside of this though, is that we don’t have the worker ready immediately for it to receive events.</p><p>So what we created instead was an event bus, which would relay events in bulk to the worker (which can also optimize the processing time needed to communicate with your worker). It keeps all the events locally and then when the worker is initialized it will send the events to the worker for processing.</p><h4>Results</h4><p>In the end, we measured a <strong>4%</strong> decrease in <a href="https://nextjs.org/docs/app/building-your-application/optimizing/analytics#custom-metrics">hydration time</a> on all our pages (with the same metrics solution) and an additional <strong>5%</strong> decrease in subsequent rendering time!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UXObUeMGSH-Bi8WyGWNeaQ.png" /><figcaption>(we renamed the TracerProvider to ClientTracerProvider, as it’s only for client-side tracing)</figcaption></figure><p>We also found more opportunities to improve our core metrics. We were including all the OpenTelemetry packages in the main bundle as it was part of all pages and the bootstrapping part of the application. Now that we moved everything related to OpenTelemetry to the Web Worker we were able to move the packages into the bundle with the worker. This bundle can be loaded after we hydrate and render the application because we communicate with our Web Worker through the event bus! This decreased our <a href="https://web.dev/fcp/">FCP</a> by another <strong>4%</strong>.</p><blockquote>We did have to make sure that no part of the app was or is referencing any OpenTelemetry packages. We had to port an enum to the main bundle for this, but the rest was easily extracted to just be used inside the Web Worker.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bpgAq37Fy2LeEcxsS2h0xg.png" /><figcaption>This was all part of the main app bundle before!</figcaption></figure><h4>Tasks where we depend on the immediate response</h4><p>We have another heavy component that is slowing down our hydration time that loads a huge JSON file which we use for deterministic experimentation for our application. You can see that it’s an even bigger part of the hydration cycle than the OpenTelemetry provider:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AIHwt-KoNSmTJ939SjVWhA.png" /><figcaption>Almost 15% of the total time!</figcaption></figure><p>Moving this to the Web Worker is a bit more complex and will be handled in part 2 of “Leveraging Web Workers for performance at HelloFresh”.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c12865b443bf" width="1" height="1" alt=""><hr><p><a href="https://engineering.hellofresh.com/leveraging-web-workers-for-performance-at-hellofresh-part-1-c12865b443bf">Leveraging Web Workers for performance at HelloFresh — Part 1</a> was originally published in <a href="https://engineering.hellofresh.com/">HelloTech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Front-end Microservices at HelloFresh]]></title>
            <link>https://engineering.hellofresh.com/front-end-microservices-at-hellofresh-23978a611b87?source=rss-7448f8460d7a------2</link>
            <guid isPermaLink="false">https://medium.com/p/23978a611b87</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[microservices]]></category>
            <dc:creator><![CDATA[Pepijn Senders]]></dc:creator>
            <pubDate>Tue, 15 Aug 2017 10:41:01 GMT</pubDate>
            <atom:updated>2017-08-15T13:18:19.490Z</atom:updated>
            <content:encoded><![CDATA[<p>About 2 years ago, one of our developers put the idea of front-end microservices into our heads. After probing him a bit on how he would like to accomplish this, he didn’t have a satisfying answer. His idea was to make a platform made up out of &lt;iframe&gt;s, but we didn&#39;t want to get into the ugly world of &lt;iframe&gt;s for our main website. It would be okay for internal tools, but not for the face of the company.</p><p>Now, after a couple of years’ thought, we have a solution. It gets pretty close to what we envisioned a front-end microservice architecture to be and, <a href="https://medium.com/@tomsoderlund/micro-frontends-a-microservice-approach-to-front-end-web-development-f325ebdadc16">since the rest of the world is also getting in on the front-end microservices train</a>, we thought we should show off our solution as well.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*66fXar8Z8D2hKXlZYV7d1A.png" /><figcaption>Alice on her way to find the perfect front-end microservice architecture</figcaption></figure><h3>Defining a front-end microservice</h3><p>Firstly we had to <em>define</em> what a front-end microservice is, so what is a front-end microservice exactly?</p><p>Our definition was as follows (again we’re not sure if this is the right definition, it’s just <em>our</em> definition):</p><blockquote>A front-end microservice listens on one or more HTTP hooks (endpoints) and serves the page HTML and links to scoped CSS and JavaScript.<br><br>The technology should be free to choose; there should be no external critical dependencies. This way we can serve React apps on one page, plain HTML on the other, and maybe a Vue app on another page.</blockquote><p>Pretty simple so far. We also came up with the following additional requirements:</p><ul><li>It should have an isolated deployment cycle.</li><li>It should be able to run in an isolated environment.</li></ul><p>And something a bit more difficult, since we want the experience for our end-users to be the most optimal:</p><ul><li>The HTML should be server side rendered.</li><li>The response time should be as fast as possible.</li></ul><h3>The design process</h3><p>We need a couple of parts to make this front-end microservices thing work. The first part we needed was actually something we already had. It’s not the most optimal solution and it’s pretty hard to configure, but it worked. Meet <em>entry</em>, an NGINX server listening on the HelloFresh domain. This routes traffic to via <a href="https://www.consul.io/">consul</a>, so it’s easy for us to launch new servers to serve these pages by simply adding a new location block:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3IT8M2Y8ef4OkhssFJG41g.png" /><figcaption>Entry server configuration for our the <a href="https://www.hellofresh.com/shop">HelloFresh shop</a></figcaption></figure><p>As you can see we also intercept our errors here (404s and 5xxs), which are then proxied to other front-end microservices that serve the error pages 💪.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JDkuqsF-VPbYtkA5IlILmA.png" /><figcaption>All the different services happily drinking tea together</figcaption></figure><h3>HelloFresh front-end microservice jargon</h3><p>Of course we’re not going to call everything a “front-end microservice”. For the sake of saved keystrokes, we came up with some HelloFresh specific jargon for our architecture:</p><ul><li><strong>Fragment (aka <em>florp</em>)</strong>: The server serving the entire HTML bulk, CSS etc. — essentially a full page</li><li><strong>Particle (aka <em>glorp</em>)</strong>: Shared parts of the page that need to be loaded synchronously</li><li><strong>Tag</strong>: Asynchronously loaded shared parts of the page</li></ul><blockquote>The back-enders were a bit confused by our ultra cool names; they had to come up with their own variations. I noted them in between the parentheses.</blockquote><h4>The fragment</h4><p>Honestly, this name came to be because we initially thought to go with <a href="https://www.mosaic9.org/">Zalando’s approach</a>. It didn’t turn out this way, but we kept the name. This is a service that runs under one of these <em>entry server </em>locations and serves small <a href="https://en.wikipedia.org/wiki/Single-page_application">SPA’s</a>.</p><p>Currently we have only one kind of <em>fragment</em> running, but soon we will have some more kinds to serve legacy pages.</p><p>This non-legacy <em>fragment</em> is built up out of an isomorphic React application, that is served through an <a href="https://expressjs.com/">Express</a> server. It’s like any other isomorphic react boilerplate, but with some of our own specific needs built in. One of these needs is the integration of the <em>particles</em>.</p><h4>The particle</h4><p>The particle is a bit special and tailored to our needs. We built the particle to create applications that are shared across our platform, but also need to be rendered on the server. A simple example is our header and footer, which are simple React applications. The cool thing here is that the parent application can be anything, since the <em>particles </em>are not dependent on the application that includes them; all they need is an element to bind to. The <em>particle</em> server returns JSON that contains the following:</p><ul><li>The HTML that is server side rendered.</li><li>The critical CSS that comes with this.</li><li>The state of the application at that point in time.</li><li>Link to the script file to mount the application</li></ul><p>So an example return would be:</p><pre>{<br>    &quot;html&quot;: &quot;&lt;div id=\&quot;react-root\&quot;&gt;Test&lt;/div&gt;&quot;,<br>    &quot;css&quot;: &quot;#react-root{color:green}&quot;,<br>    &quot;initialState&quot;: {<br>        &quot;country&quot;: &quot;DE&quot;<br>    },<br>    &quot;script&quot;: &quot;<a href="https://assets.hellofresh.com/dist/my-particle.hash.js">https://assets.hellofresh.com/dist/my-particle.hash.js</a>&quot;<br>}</pre><p>The <em>fragment</em> will basically request this <em>particle</em>, write the HTML and CSS and when the application is loaded <a href="https://stackoverflow.com/questions/31556450/what-is-mounting-in-react-js">mount</a> the particle. There’s a lot more to it than that — a bit too much to talk about in this blog post. Watch this space for a future article dedicated to this <em>glorp</em> technology.</p><h4>The tag</h4><p>The <em>tag</em> is also something of our creation. We felt the need for some very small — but still a bit more complex than easy jQuery and CSS — applications that are triggered asynchronously on the page. Consider a modal that you want to load asynchronously — perhaps on a button press — and is not necessarily tied to a route. Using a tag instead of a particle, loading of these scripts is deferred, so doesn’t affect page load.</p><p>These are implemented as small React applications that expose a window function to initialise them. For example, to initialise the sign up <em>tag</em> we built:</p><pre>window.signUpTag({<br>    bindToElement: document.body,<br>});</pre><p>This function will then load the React application and mount it to the element.</p><h3>How it all ties together</h3><p><em>Will it blend!?</em> Well we think so:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*y6nhMfo3JlGEDjwqSY_u4g.png" /><figcaption>Simplified view of the current infrastracture</figcaption></figure><p>In the flow above, a client sends a request for one of our React pages. If the CDN doesn’t have a recent cached copy of this page, it will direct the request to our entry server which will then forward it to one of our <em>fragments</em>. I drew the RAF (recipe archive fragment), the FF (funnel fragment) and the MDF (my deliveries fragment) here to portray a part of our infrastructure. The <em>fragments</em> in turn will request parts (<em>particles</em>) of the page from the gateway. These <em>particles</em> will return the HTML and CSS needed for the fragment to return a full render. As fragments are cached by the CDN the experience is lightning fast ⚡️.</p><p>Here’s another view of the same flow:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_E0GZUrr9Q2kkwaXaK0dZA.png" /></figure><h3>The road travelled</h3><p>We’ve now been working with this architecture since the Summer of 2016. Luckily the way we built it, it allows us to constantly evolve this architecture. We’re able to iterate on it really quickly due to a project manager we built in-house, called 🐙. Now, after almost a year, we’ve gathered a lot of experience and data on how this approach has worked for us.</p><h4>Development velocity</h4><p>At first, we found development speed to be a bit slow. Since this approach requires you to own your entire project E2E, the learning curve can be steep. But after cresting the curve we were able to push out projects like never before; the new <a href="https://www.hellofresh.com/shop/">HelloFresh shop</a> for example took 3 people 4 weeks to roll out. Our developers also claim increased confidence and breadth of knowledge after adopting this approach.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qVHCVE7kDT5PraIhJsQKaA.png" /><figcaption>Tiago didn’t do a lot</figcaption></figure><h4>Isolation and code freedom</h4><p>At the beginning of this project, one aim was to be able to write projects in Vue, React, or Angular and to have it work seamlessly with the rest of the website. While this is possible, in practice everyone has adhered to our React boilerplate so far. Of course, the fact that we have the possibility is already a big win in flexibility and future-proofing.</p><h4>Error tracing</h4><p>Fragments’ logging is unified, consistent, and fairly verbose. With all the applications following our boilerplate, we’ve gained a lot of speed in triaging issues as they arise, which helps us to fix issues much more rapidly.</p><h4>Development</h4><p>This win is probably the most noticeable. While previously we had this huge monolithic application that was unmaintainable and nigh-on unbootable in a local environment (shortly described in our previous blog post <a href="https://engineering.hellofresh.com/javascript-con-carne-15ec24baacbe">here</a>), now each project has its own server, and its isolated dev environment. All the external dependencies can be configured, so you can talk to the live, staging, or your own environment to debug problems.</p><h3>Credits</h3><p>Credit goes to <a href="https://medium.com/u/3e1b2ee47bd3">Tom Söderlund</a><a href="https://medium.com/@tomsoderlund/micro-frontends-a-microservice-approach-to-front-end-web-development-f325ebdadc16"> blog post</a>, wherein he describes the approaches other companies have taken to front-end microservices. I think it’s also important to showcase all the benefits we got from this approach at HelloFresh.</p><p>I couldn’t have built this without the help of a lot of my colleagues. I need to thank all the front-end developers, who hung in there during many intense patch periods, the front-end leadership team for being critical about the process and the implementation, <a href="https://medium.com/u/93e129fcb710">Joanne Wong</a> for making the beautiful graphs in this blog post and last but not least to <a href="https://medium.com/u/1ee3e6fb624f">Nuno Simaria</a> for giving us the freedom to develop this infrastructure next to an already full roadmap.</p><p><em>Looking for a new job opportunity? </em><a href="http://grnh.se/7x5o0u1"><em>Become a part of our team! We are constantly on the lookout for great talent</em></a><em>. Help us in our mission of delivering fresh ingredients to your door and change the way people eat forever.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=23978a611b87" width="1" height="1" alt=""><hr><p><a href="https://engineering.hellofresh.com/front-end-microservices-at-hellofresh-23978a611b87">Front-end Microservices at HelloFresh</a> was originally published in <a href="https://engineering.hellofresh.com/">HelloTech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A/B Testing with Redux]]></title>
            <link>https://engineering.hellofresh.com/ab-testing-with-redux-10ea45c22c5f?source=rss-7448f8460d7a------2</link>
            <guid isPermaLink="false">https://medium.com/p/10ea45c22c5f</guid>
            <category><![CDATA[react]]></category>
            <category><![CDATA[redux]]></category>
            <category><![CDATA[a-b-testing]]></category>
            <dc:creator><![CDATA[Pepijn Senders]]></dc:creator>
            <pubDate>Tue, 11 Jul 2017 11:10:08 GMT</pubDate>
            <atom:updated>2017-07-12T14:12:00.476Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RGNE6ItiR6kKHgqU9fsEtw.png" /><figcaption>Split test on two tectonic plates in Thingvellir, Iceland (winner still undefined)</figcaption></figure><p>We think every developer has a ❤️/👿 relationship with A/B testing. It’s either about loading bulky scripts with jQuery written by marketing people, or it’s about having to hack something into your own code. It’s never really nice or cool, it’s always a drag.</p><p>We at HelloFresh wanted to make at least some part of it easier to handle, to keep the disruption from A/B tests to our shiny React / Redux apps to a minimum. This is when we came up with external alterations.</p><p>The idea basically sprung from changing the state of the store from a window function. But since we didn&#39;t want to add a reducer for everything and we also didn&#39;t want to expose every action in our application, we had to think of something special.</p><h3>The kernel</h3><p>First we have to add our actions to the window object in an elegant way. We came up with the fancy word ExternalActionKernel, which is basically a very simple React component.</p><pre>import { Component } from &#39;react&#39;;<br>import { connect } from &#39;react-redux&#39;;</pre><pre>import { setAlteration } from &#39;../../store/actions/externalAlterations&#39;;</pre><pre>class ExternalActionKernel extends Component {<br>    componentDidMount() {<br>        window.triggerExternalAction = (action, payload) =&gt; {<br>            return this.props[action](payload);<br>        };<br>    }</pre><pre>    render() {<br>        return null;<br>    }<br>}</pre><pre>const mapDispatchToProps = { setAlteration };</pre><pre>connect(null, mapDispatchToProps)(ExternalActionKernel);</pre><p>And then to execute this action you would do something like:</p><pre>window.triggerExternalAction(&#39;setAlteration&#39;, {<br>    showTestModal: true<br>});</pre><p>The alteration reducer basically holds all the alteration information. We’re not going to bore you with the code there, but it’s basically an object that gets updated through actions. The payload can be any kind of object.</p><h3>Using the alterations</h3><p>Now to use this alteration that we just set, we need to connect our component where we would show this: TestModal.</p><pre><br>import React, { Component } from &#39;react&#39;;<br>import { connect } from &#39;react-redux&#39;;</pre><pre>class TestComponent extends Component {<br>    render() {<br>        if (this.props.showTestModal) {<br>            return &lt;div&gt;AB Test&lt;/div&gt;;<br>        }</pre><pre>        return &lt;div&gt;Normal&lt;/div&gt;<br>    }<br>}</pre><pre>connect(state =&gt; ({<br>    showTestModal: state.alterations.showTestModal,<br>}))(TestComponent);</pre><p>Look how elegant that is! No more:</p><pre>$(&#39;.ab-test-modal&#39;).show()</pre><p>The only thing the asynchronous scripts have to do now is call this window function with the right parameters and you have an A/B test!</p><h3>Example</h3><p>You can play around with our website a bit as well. For example, on our shop page you can change the highlighted product by using this technique.</p><p>You can even try yourself, in your browser! Simply go <a href="https://www.hellofresh.com/shop/">here</a>, pop open your console, and execute the following:</p><pre>window.triggerExternalAction(&#39;setCategoryPromotedProductFamily&#39;, {<br>    category: &#39;kits&#39;,<br>    productFamily: &#39;slice-n-dice-kit&#39;,<br>});</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/851/1*nQdAvSm33juvAFSEr7-Lkg.gif" /><figcaption>Or if you’re feeling too lazy, you can look at this GIF</figcaption></figure><p>🎉🎉🎉🎉🎉</p><p><em>Looking for a new job opportunity? </em><a href="http://grnh.se/7x5o0u1"><em>Become a part of our team! We are constantly on the lookout for great talent</em></a><em>. Help us in our mission of delivering fresh ingredients to your door and change the way people eat forever.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=10ea45c22c5f" width="1" height="1" alt=""><hr><p><a href="https://engineering.hellofresh.com/ab-testing-with-redux-10ea45c22c5f">A/B Testing with Redux</a> was originally published in <a href="https://engineering.hellofresh.com/">HelloTech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The css-in-js battle]]></title>
            <link>https://engineering.hellofresh.com/the-css-in-js-battle-89c34a7a83ea?source=rss-7448f8460d7a------2</link>
            <guid isPermaLink="false">https://medium.com/p/89c34a7a83ea</guid>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[jsx]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[css]]></category>
            <dc:creator><![CDATA[Pepijn Senders]]></dc:creator>
            <pubDate>Thu, 08 Dec 2016 12:26:02 GMT</pubDate>
            <atom:updated>2016-12-08T13:54:36.205Z</atom:updated>
            <content:encoded><![CDATA[<p>At HelloFresh we have been in a constant fight with our styles. A year ago we just had bad, messy, and mostly just a lot of CSS like many other companies. We wanted to change this so we decided to check out one of the new kids on the block and the coolest thing at that time was css-in-js.</p><p>We ended up going with A<a href="https://github.com/Khan/aphrodite">phrodite</a>, because:</p><ul><li>Cool name.</li><li>Khan Academy built it, so they probably also use it.</li></ul><p>This was mostly it for us actually, the other options didn’t suit our criteria well. Like some of them were bound to another view framework, for example how <a href="https://github.com/FormidableLabs/radium">Radium</a> is bound to React.</p><p>Now, some months later we decided to revise our decision (read up on how we built the entire application <a href="https://medium.com/hellofresh-dev/javascript-con-carne-15ec24baacbe">here</a>). We were happy with the choice we made, but we wanted to see what new stuff was out there. Luckily at the time we built it we decided to have some kind of interface layer between what was actually output to our component’s <em>classNames </em>and what library we actually used. So the refactoring part will not be too difficult (<em>hopefully</em>).</p><p>The contenders were (as far as we could find them):</p><ul><li><a href="http://github.com/Khan/aphrodite">Aphrodite</a>, still going strong, still a cool name.</li><li><a href="https://github.com/threepointone/glamor">Glamor</a>, really really like the rule based API.</li><li><a href="https://github.com/cssinjs/jss">JSS</a>, awesome logo, nice API, can also do nested styles.</li><li><a href="https://github.com/jxnblk/cxs">CXS</a>, functional CSS, say WHUT!?</li></ul><h3>The battle</h3><p>We put all the libraries up against each other and checked out their bundle sizes, API’s and performance.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DbcPp5xjGS2dTylsx6Cr9w.jpeg" /><figcaption>Something like this</figcaption></figure><h3>Performance</h3><p>The first test we ran was just a simple case:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/09777348065ab89eebea41009df15fd2/href">https://medium.com/media/09777348065ab89eebea41009df15fd2/href</a></iframe><p>This is of course a very simplified version and it won’t work, because style-lib is not a thing. But it shows the way we tested pretty well.</p><p><em>For the benchmarks, we were testing on a Macbook Pro with a good amount of memory and CPU, and ran the tests with the help of </em><a href="http://benchmarkjs.com/"><em>Benchmark.js</em></a><em>.</em></p><h4>Try 1</h4><p>These are the results of the first test we ran:</p><pre>5 tests completed.<br><br>aphrodite          x  3,178 ops/sec ±13.32% (43 runs sampled)<br>jss                x    107 ops/sec ±67.27% (8 runs sampled)<br>jss-without-preset x  71.44 ops/sec ±7.87% (45 runs sampled)<br>glamor             x 11,968 ops/sec ±9.31% (50 runs sampled)<br>cxs                x  7,774 ops/sec ±9.49% (56 runs sampled)<br><br>Fastest is: glamor</pre><p>As you can see JSS was slow, very slow. Tiago, one of our front-end developers, created an issue on the repo, which you can read up on <a href="https://github.com/cssinjs/jss/issues/364">here</a>. Turns out we were writing on top of the global JSS object all the time, without resetting it. Seems like the default implementation is not that stateless.</p><p>Luckily they had a fix for us so we could create a new instance for every test. The new results were a bit more in line with what we expected:</p><pre>5 tests completed.<br><br>aphrodite          x  6,088 ops/sec ±7.65% (62 runs sampled)<br>jss                x 11,291 ops/sec ±8.39% (75 runs sampled)<br>jss-without-preset x 11,622 ops/sec ±17.20% (52 runs sampled)<br>glamor             x  6,623 ops/sec ±14.67% (67 runs sampled)<br>cxs                x 12,121 ops/sec ±3.80% (75 runs sampled)<br><br>Fastest is: cxs,jss</pre><p>Along with creating class names we were also rendering the HTML with the help of React components. We found this to be an unnecessary constant through our tests and continued with just rendering HTML as a string. As you can the overall performance increased as well.</p><p>We ran the tests one more time and this time also with <a href="https://github.com/jxnblk/cxs/blob/5597e1be924a2d94967a7a619dfd2ba6ccf94516/src/optimized.js">cxs/optimized</a>, a library by CXS that is supposed to have better performance on the browser.</p><p>We decided to start measuring string length as well on the output from the library. This will include the rendered HTML with the CSS.</p><pre>aphrodite          length 470<br>jss                length 447<br>jss-without-preset length 439<br>glamor             length 422<br>cxs                length 400<br>cxs-optimized      length 445</pre><pre>  6 tests completed.<br><br>  aphrodite          x  8,943 ops/sec ±14.55% (68 runs sampled)<br>  jss                x 11,697 ops/sec ±27.81% (55 runs sampled)<br>  jss-without-preset x 46,684 ops/sec ± 7.89% (62 runs sampled)<br>  glamor             x  5,042 ops/sec ±14.27% (47 runs sampled)<br>  cxs                x 19,122 ops/sec ±10.24% (69 runs sampled)<br>  cxs-optimized      x 12,843 ops/sec ±10.52% (69 runs sampled)<br><br>Fastest is: jss-without-preset</pre><h4>Expanding</h4><p>Of course just testing one class is probably a piece of cake for every library. So we turned it up a notch and added one test that added a lot of classes with the same styles. And another one that added a lot of classes with different styles (a simple incremental <em>paddingLeft</em>).</p><p>With these new tests we are also evaluating which libraries manage to keep their output the leanest.</p><h4>The classes overload test</h4><pre>aphrodite          length 3044<br>jss                length 3085<br>jss-without-preset length 3064<br>glamor             length 1935<br>cxs                length 1943<br>cxs-optimized      length 1943</pre><pre>  6 tests completed.<br><br>  aphrodite          x 1,145 ops/sec ±23.06% (55 runs sampled)<br>  jss                x 1,305 ops/sec ±31.53% (39 runs sampled)<br>  jss-without-preset x 2,723 ops/sec ±17.48% (38 runs sampled)<br>  glamor             x 2,698 ops/sec ±18.00% (48 runs sampled)<br>  cxs                x 1,697 ops/sec ±15.10% (46 runs sampled)<br>  cxs-optimized      x 2,359 ops/sec ± 7.45% (72 runs sampled)<br><br>Fastest is: jss-without-preset,glamor</pre><h4>The styles overload test</h4><pre>aphrodite          length 3594<br>jss                length 3509<br>jss-without-preset length 3430<br>glamor             length 3298<br>cxs                length 3022<br>cxs-optimized      length 3063</pre><pre>  6 tests completed.<br><br>  aphrodite          x   853 ops/sec ±19.51% (54 runs sampled)<br>  jss                x 2,200 ops/sec ±10.85% (66 runs sampled)<br>  jss-without-preset x 4,301 ops/sec ±17.48% (55 runs sampled)<br>  glamor             x   665 ops/sec ±17.53% (56 runs sampled)<br>  cxs                x 1,032 ops/sec ±24.12% (43 runs sampled)<br>  cxs-optimized      x   743 ops/sec ±21.16% (45 runs sampled)<br><br>Fastest is: jss-without-preset</pre><p>On class merging CXS and Glamor are doing great jobs, but when the styles are different everyone is doing an equal job at keeping it lean. Only CXS is a bit better than the rest.</p><p>All libraries don’t merge classes on a higher level (when the styles are the same), you can see that here:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0MoPBqfysuXyb8zW_Dwlrg.png" /><figcaption>This can of course be minified a lot (example from CXS)</figcaption></figure><p>Looking at the operations per second, JSS without preset is definitely winning on all sides and Glamor is getting into trouble when there are different styles at play. Since you would actually use JSS with a preset the library that won this challenge is a close call. CXS is definitely a good contender overall, but JSS with the default presets is still a good runner-up and they managed to get the core performance without presets very tight.</p><p>Winner: <strong>CXS</strong></p><h3>Bundle sizes</h3><p>Of course all this processing power is worth nothing if the library is huge, so next what we did is check what the sizes were when bundled by Webpack. These are the results:</p><pre>Size cxs 9.766KB<br>Size cxs-optimized 12.668KB<br>Size jss-without-preset 24.183KB<br>Size jss 37.04KB<br>Size aphrodite 18.919KB<br>Size glamor 35.436KB</pre><p>As you can see, JSS is the fastest but also one of the biggest. The results of CXS are also really good, they really do what they say. Functional libraries should be small. Glamor say in their README that they are small as well, but actually they aren’t that small when you compare them to the rest.</p><p>Winner: <strong>CXS</strong></p><h3>API</h3><p>Having a good processing time and a small bundle size are very important, but we also need to work with these libraries every day. The API of the library is just as important an aspect.</p><p>We made some showcases of how the API’s are used in their simplest forms.</p><h4>Aphrodite</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1f35933368d6d52ab5f128401263e183/href">https://medium.com/media/1f35933368d6d52ab5f128401263e183/href</a></iframe><p>Aphrodite is very verbose and they are keeping a close relationship with actual CSS. The API uses words like <em>StyleSheet</em> and <em>css</em>. The <em>renderStatic</em> method is a method to render the CSS as a string.</p><h4>CXS</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/72d3a70a49ce40305f800650f7123f9a/href">https://medium.com/media/72d3a70a49ce40305f800650f7123f9a/href</a></iframe><p>We really like this, because you don’t really care about classNames and naming things anymore, you just create rules and attach these to components. Also the reference of the styles in the form of a string is created on the fly, so no need to render anything.</p><h4><em>Glamor</em></h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/804533b6c88bc9cd0c7251377bab8a45/href">https://medium.com/media/804533b6c88bc9cd0c7251377bab8a45/href</a></iframe><p>Glamor’s API shares elements with the APIs of CXS and Aphrodite. Glamor also requires HTML to be returned in a callback function.</p><h4><em>JSS</em></h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/fd1da7133ecada6266e51bed721b5c2e/href">https://medium.com/media/fd1da7133ecada6266e51bed721b5c2e/href</a></iframe><p>The API of JSS is a bit harder to grasp. It uses a little bit of the way Aphrodite works and the way they render the CSS is more like CXS.</p><p>Winner: <strong>CXS</strong></p><h3>Integration with the client</h3><p>The previous tests only take rendering code on the server into account. When you render your CSS on the server you of course don’t want to execute the same operations on the client. So which library takes the best care of this? Well calculating it with benchmarks is actually pretty hard; since you’re going from your server to the client we would have to make some crazy PhantomJS contraption. But we thought probably explaining and checking out what each library does to solve this is probably a better idea.</p><p>Both <strong>Aphrodite</strong> and <strong>Glamor </strong>are careful to rehydrate before bootstrapping on the client; Glamor explains how to do so very well <a href="https://github.com/threepointone/glamor/blob/master/docs/server.md">here</a>. However neither <strong>CXS </strong>nor <strong>JSS</strong> really care about what was already rendered on the server. The instruction from CXS, for example, is to just remove the style tag that was put there by the server after the client is done generating all the CSS again.</p><p>Winner: <strong>Glamor</strong></p><h3>Conclusion</h3><p>There’s a lot of libraries out there and they all do it a bit differently. Since writing the first draft of this blog post, we already got numerous <a href="https://github.com/hellofresh/css-in-js-perf-tests/pulls?utf8=%E2%9C%93&amp;q=is%3Apr%20">Pull Requests</a> of more css-in-js libraries and we created several issues for the libraries that we documented here. Some library creators actually also pointed us to better ways of using their library, which was really helpful.</p><p>I think there’s still no clear winner for us. Definitely the rehydrating is an issue, as we tend to have a lot of styles and having to re-generate them again for one page seems a bit wasteful. I created <a href="https://github.com/cssinjs/jss/issues/379">an issue</a> on the JSS repository about this and the maintainer has some good points on the topic. We do hope that the aftermath of this battle is going to be of some help at least!</p><p>Oh yeah, and don’t forget to <a href="https://github.com/hellofresh/css-in-js-perf-tests">check out the source code for the benchmark tests</a>! 🌟 (Feel free to contribute with more css-in-js libraries as well!)</p><p><em>Looking for a new job opportunity? </em><a href="https://www.hellofresh.com/jobs/"><em>Become a part of our team! We are constantly on the lookout for great talent</em></a><em>. Help us in our mission of delivering fresh ingredients to your door and make home cooking accessible to </em><strong><em>everyone</em></strong><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=89c34a7a83ea" width="1" height="1" alt=""><hr><p><a href="https://engineering.hellofresh.com/the-css-in-js-battle-89c34a7a83ea">The css-in-js battle</a> was originally published in <a href="https://engineering.hellofresh.com/">HelloTech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[JavaScript con Carne]]></title>
            <link>https://engineering.hellofresh.com/javascript-con-carne-15ec24baacbe?source=rss-7448f8460d7a------2</link>
            <guid isPermaLink="false">https://medium.com/p/15ec24baacbe</guid>
            <category><![CDATA[redux]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[isomorphic-applications]]></category>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[Pepijn Senders]]></dc:creator>
            <pubDate>Mon, 21 Nov 2016 10:21:01 GMT</pubDate>
            <atom:updated>2016-11-21T19:12:55.295Z</atom:updated>
            <content:encoded><![CDATA[<p>About 1 year ago our UX department was assigned with the task of revising our much aged account area, which was built and designed on the fly by new developers like me. I, Pepijn, am the head of all front end business at HelloFresh and also a generally tall person, who in part (speaking for myself) had no clue of all the crazy corner cases in our intricate business model when I started at HelloFresh.</p><p>This was a time where Angular 1.2 applications were still the world standard for single page applications. Coming from an environment that was built with rogue JS files filled with jQuery that themselves were then in Yii1 templates, the Angular application was already a big improvement (we also were just <strong>3 front-end developers</strong> at the time, in the entire company!). We decided that before we would let the bug reports from this Angular application drive us up the wall, we would totally revise it from the bottom-up. That brings me to the main theme of this blogpost, <strong>JavaScript con Carne:</strong> a wink to the multi-cultural team and hardcore development period we embarked upon to phase out this angular application that we created.</p><p>We at HelloFresh also like to codename our projects with food, because we like food. The codename of the angular application was „<strong>Ginger</strong>“, the new application con Carne: „<strong>Senf</strong>“. Which means Mustard in German and for the ease of reading let’s dub the Yii fiasco that was there before the „<strong>Brussels Sprout</strong>“ era, because no one likes Brussel Sprouts.</p><p>I will try to make the journey as clear as possible from the beginning, so you have a better context, so you can try and understand our decisions better.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RplNIwBkRILqkUnf5_xIMA.jpeg" /><figcaption>Wedges and Schnitzel with a nice Senf dressing © HelloFresh DE</figcaption></figure><h4>Brussels Sprout (2012 – Summer 2015)</h4><p>If you have experience with working for or working with or doing anything with <a href="https://www.rocket-internet.com/">Rocket</a> companies that were bootstrapped in this time, you probably heard of Alice and Bob.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-vsJqY78GVYGMUJ89NF8Xw.jpeg" /><figcaption>Alice (left) and Bob (right) © Huffingtonpost</figcaption></figure><p>If you did, you can skip this short introduction:</p><blockquote>Alice is a ~5 year old PHP framework written with the dark horror that is known as Yii framework 1. This PHP framework was there to generate all the views and pages and not to contain too much logic. Of course that is already where it goes wrong in most companies, these things become bulky as hell and since only front end developers were wriggling around in these parts of the code, you can probably imagine how bad the PHP was…</blockquote><blockquote>Alice, along with Bob (a Zend Framework 1 application; her best friend, partner, or worst enemy — you choose!), does a couple of things. It holds the core business model, which was basically a subscription model hacked into an e-commerce store application, and it’s also a CMS and a management system for anything customer related. I should probably also mention, that the version we were using was basically an alpha version of the Bob and Alice system. It was already broken by design.</blockquote><p>So basically everything on the front end was inside Alice, so our first account area, „<strong>Brussels</strong> <strong>Sprout</strong>“ as well. It was a beautiful thing with gradient header background and content bubbles used way more than necessary.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vuU7FL29ryzfDXfwZVocWw.jpeg" /><figcaption>Tasty chicken with disgusting roasted Brussels Sprouts © HelloFresh US</figcaption></figure><p>Small side note: I myself have never really worked a lot on these systems (luckily).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cDhZvMSdm8g-ljo5_C5Reg.jpeg" /><figcaption>The actual old account area</figcaption></figure><h4>Ginger (Launch: Summer 2015)</h4><p>Yes you read that title right: if you’re still early enough to read this, you might be able to catch the old account area in the wild. For most of the countries we are still in a testing phase, but it’s safe to say it will be extinct very soon!</p><p>When I joined HelloFresh, the guys already started the Angular 1.2 applications. We had a short timeline and a lot of stuff to do. For me it wasn’t my first angular SPA, for the guys it was. I never built anything this big though!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KJpXH4BuoSZbFsSNdNXSIA.jpeg" /><figcaption>Steak glazed with some nice ginger © HelloFresh DE</figcaption></figure><p>However, sometimes you have to know when to <a href="http://www.ccs.neu.edu/course/cs5500f14/Notes/Prototyping1/planToThrowOneAway.html?">throw one away</a>. Which brings us to:</p><h4>Senf (Launch: Summer 2016)</h4><p>As the name indicates, Senf is here to introduce a new spice in HelloFresh. I won’t spend a lot of time on the design part since this is an engineering blog. But safe to say the UX team did an amazing job at fine-tuning this.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uf7zrw8-d9ikeNZWC6auxQ.jpeg" /><figcaption>Meal swap view (left Senf, right Ginger) © HelloFresh</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MEgmZimPby_zH3HRTiQF_g.jpeg" /><figcaption>Calendar view (left Senf, right Ginger) © HelloFresh</figcaption></figure><h4>Why was the old so wrong?</h4><p>Well:</p><ul><li>Response time for the subscription page in the account area was around 14 seconds on a good DSL connection.</li><li>The old code had so many corner cases hacked in, meaning one little change could throw over the entire Jenga Tower.</li><li>Angular 1.x was nice when we didn’t know any better. The amount of watchers to keep track of the data and update the DOM were insane. Also 500 line directives were not a rarity.</li><li>We were loading around 3MB’s of JavaScript onto the client, without gzip and without a CDN. So you can imagine how slow this was, and how costly for users using a limited data plan.</li><li>To develop on this application that you had to run a vagrant box (which took a day to provision in the good old days) which contained everything — and when I say everything, I really mean everything. So API’s, back-end tools, forecasting tools — and you have to run all this just to change a small thing on the front-end. Those times were pretty sad.</li></ul><p>So how do we prevent all these things, whilst also implementing a totally different design? Well, there were actually a lot of solutions out there before the summer of 2016, when we started. Decisions had to be made quickly and had to be made concisely. There wasn’t much room to change since we didn’t even convince business yet that the new project is viable.</p><h4>The team</h4><p>I haven’t mentioned yet that HelloFresh is a very back-end heavy company. We have lots of PHP and Go developers, but sadly we were still lacking resources on the front-end side. To implement „<strong>Senf</strong>“, we had the following warriors:</p><ul><li>1 Mexican developer (<a href="https://github.com/josemussa">@josemussa</a>)</li><li>1 Portuguese developer who wasn’t even in the office, since he’s in the <a href="http://www.remoteyear.com/">remote year</a> program (<a href="https://github.com/tiagojsalmeida">@tiagojsalmeida</a>)</li><li>2 weeks of help from our very own HelloFresh Australian from another team, working on customer related topics (<a href="https://github.com/MichaelFBA">@MichaelFBA</a>)</li><li>And me, Head of Front-end, <a href="https://www.youtube.com/watch?v=TmSr1_Y2AYg">Dutch</a> and mostly busy keeping the rest of the front-end for the company running as well (<a href="https://github.com/PepijnSenders">@PepijnSenders</a>)</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kL46zoP1XDKPc83Su2qSAw.jpeg" /><figcaption>Fuel of choice, the highly caffeinated Club Mate</figcaption></figure><h4>The server</h4><p>The first choice that was clear is that we wanted to have a universal code for our components and that we need to able to render as much as possible on the server. Having JavaScript on both the front-end and back-end we could avoid most of our problems originated from working on our out-dated PHP back-end.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DN7ToydkJZEdVaJVK_Nhvw.png" /><figcaption>JS for the win</figcaption></figure><p>Again, easier said than done, getting a grip of a methodology as new at the time as SSR (<em>Server-Side Rendering</em>) is really hard. We had to do a lot of mind maps, to be able to get a grip of what we actually wanted to build and how we wanted to build it.</p><p>For the back-end router we choose Express. I know there’s a lot of Hapi fans out there and I have to say I’m becoming a bit of a fan myself. But at the time Express was definitely the right choice since we were the most familiar with it.</p><h4>The view layer</h4><p>This was a struggle as all that any of us really knew was maybe Backbone and Angular 1.x, both not really suitable for what we wanted to accomplish. The view layer, must definitely be able to be rendered on both back-end and front-end. Another thing we wanted to get rid of was this watcher soup that was a common thing in our Angular 1.x applications. The technologies that we had considered were:</p><ul><li><a href="http://riotjs.com/">Riot</a> (it’s small, it’s fast, it’s simple)</li><li><a href="https://facebook.github.io/react/">React</a> (huge community and API, used by Facebook)</li><li><a href="https://vuejs.org/">Vue</a> (bit of an unknown player at the time, but should be a smaller step to take from angular 1.x)</li><li><a href="http://elm-lang.org/">Elm</a> (big fan, but big learning curve, sadly no time or resources for this)</li><li><a href="https://angular.io/">Angular 2</a> was obviously also considered (no proper RC at the time though and we all felt pretty uncomfortable using TypeScript)</li><li>… and a couple more that were not worth mentioning for this story</li></ul><p>In the end we went for <strong>React, </strong>simplest explanation why is: <em>the amount of documentation, ease of reading through the source code, and the community backing it</em>. We personally still think that whatever library you’re going to use, <strong>if it has no community behind it’s probably better to look somewhere else</strong>.</p><p>No one of us had any experience with React other than the usual „playing around with it“. So we ordered ourselves some <a href="https://egghead.io/">Egghead.io</a> videos and started binge watching all the react related stuff out there.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*St5crFq6GyphKeEs6ak96w.png" /><figcaption>The almighty</figcaption></figure><h4>State</h4><p>When you say React, you said Flux, but at the time people were actually already <em>dispatching</em> to redux. We didn’t really know much about both to be honest, but YouTube was another great help here. We taught ourselves all the new patterns and decided that <strong>Redux </strong>was the best option for our unborn application.</p><p>What attracted us the most was the single source of truth we could create with <strong>Redux</strong>, this would definitely help us to prevent a watcher soup and a ridiculous amount Angular Services holding our data.</p><h4>The kick-off</h4><p>After making these three base decisions, I jet started the server and development tools. We went for <a href="https://webpack.github.io/">Webpack</a> in combination with our own <a href="http://github.com/hellofresh/babel-preset-senf">babel</a> and <a href="http://github.com/hellofresh/eslint-config-senf">ESLint</a> configuration. I created a development wrapper for the React/Redux application so the other developers didn’t have to worry too much about the SSR part.</p><blockquote>Not worrying about the SSR part seemed like a good idea in the beginning, but at some point you’re going to run into problems that you simply don’t have inside the development environment. Stuff like non matching output between client &amp; server, because stuff was for example done according to window attributes (bad idea anyways). We currently have the SSR built into the project itself, duplication over depending!</blockquote><p>One very smart decision looking at the process afterwards was to start with mapping out the dependencies and actions of all the components by looking at the wireframes the UX team gave us. After we did this we started modelling, very basic and with <strong>no styling at all! </strong>This choice let us catch a lot of gaps in the provided designs, before we even started thinking about CSS.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/347/1*lKiuNNm4QH-Htspiedrnlg.png" /><figcaption>Beautiful, is it not?</figcaption></figure><p>All this was mostly a one man game by @josemussa, with some help from @MichaelFBA and supervised by me. Progress was smooth, focus was on performance, we wanted to make this stuff fly!</p><h4>Styling</h4><p>If we had to style something fast and beautifully we always asked @tiagojsalmeida, our remote worker, to do some CSS magic. But we were nowhere near convinced of the style loaders that were used in Webpack at the time.</p><p>We were afraid of having a huge dependency on Webpack, since it was kind of new and the JS files that would require the CSS files would subsequently be unreadable by any normal JS engine. Also loading the CSS as part of a JS dependency felt a bit weird for us, we wanted to be more in control of our CSS and the way to do that was to make the CSS more programmable.</p><p>After some research we found the <a href="https://speakerdeck.com/vjeux/react-css-in-js">new css-in-js techniques</a>. We were all pretty intrigued by its latest developments. Again another technology that was just on the horizon and there was almost no writing about it.</p><p>The good thing at this point in time was that the entire account area was already there, every component was working and all the logic already clicked. We could easily test out these technologies on this bare bone frame.</p><p>So we finally got the help from @tiagojsalmeida on this project too, I know he and the rest of the team had some trouble wrapping their heads around css-in-js, but I was really convinced this could be the salvation to a couple of problems we had in the past:</p><ul><li>SSR with css-in-js works really well.</li><li>This will finally make CSS have a bigger part in the code reviewing process, before CSS was always overlooked and left behind.</li><li>Way less files and boilerplate, the account area with all its components, actions, reducers and configuration only contains around 250 files.</li><li>Better exportable components that already contained all the attached styles.</li><li>Freedom of how to push your CSS to the client, because it is now programmable.</li></ul><p>Of course with all the <em>young</em> options out there, we still had to make a decision on this, good or bad. It was very early to know which one was the good one, when most of the solutions we looked at had fewer than 500 stars at the time and only a handful of issues and pull requests.</p><p>In the end we decided to go for the solution that looked the best and that was probably already battle tested: <a href="https://github.com/Khan/aphrodite">Aphrodite</a>. Since it was built by Khan Academy we figured they must use it in production as well.</p><p>Still, we weren’t 100% sure of our decision so we decided to build an interface layer which most of the css-in-js libraries would work with. The only thing we needed to change was the implementation inside the interface and not the implementation in every component.</p><h4>Going live</h4><p>We have an internal joke in our team that you hopefully won’t mind me sharing. You can find the joke about us pushing a project live on youtube <a href="https://www.youtube.com/watch?v=EGikhmjTSZI">here</a>.</p><blockquote>Probably you will hate me now for putting that song in your head. Sorry, not sorry.</blockquote><p>So after developing the full application there were a couple of things to wrap up on, deploying the entire thing, security, assets caching, a CDN and performance.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*unKocHvloyA_AI5daVEnxg.jpeg" /><figcaption>Indeed.</figcaption></figure><p>Setting up the express server was pretty easy. Some nice middlewares we used were for example: <a href="https://github.com/helmetjs/helmet">helmet</a> for security, some <a href="https://github.com/expressjs/compression">gzip compression middleware</a> and some more logging and stats tools.</p><p>I followed this guide on medium by <a href="https://medium.com/u/5829a67c85f9">Andrey Okonetchnikov</a>, found <a href="https://medium.com/@okonetchnikov/long-term-caching-of-static-assets-with-webpack-1ecb139adb95#.2m0swtltl">here</a>, for how to generate the Webpack chunks. Our application didn’t depend that heavily on <em>require.ensure </em>before, but I can definitely recommend considering this from the start!</p><p>The CDN was the latest addition. Actually the application was already live for a couple of weeks before we added this on top of it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*MTPaT_isavb3HAZjc3x8Mg.gif" /><figcaption>Performance throttled to a DSL connection (with cache enabled and disabled)</figcaption></figure><p>As you can see we sadly still have a big dependency on our API layer (it’s pretty slow) and as you can see without caching the first response from the node service is also pretty slow. However, it’s a vast improvement on what came before:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1013/1*_hg-RsV5CwdTVPrNXbxvgQ.gif" /><figcaption>Where we came from, also on a throttled DSL connection</figcaption></figure><h3>The road ahead</h3><p>We are convinced that these new techniques will give us the power to make a super fast website and on top of that also make it more enjoyable to build and maintain.</p><p>The next step will most probably be moving our homepage to this technology, whilst improving the underlying infrastructure for these applications as well. I can recommend reading up on the tools and infrastructure that <a href="https://medium.com/u/b171999ca16c">Zalando Tech</a> built, more on that <a href="https://www.mosaic9.org/">here</a>. Also, having some kind of in the middle API layer is one of the things we will definitely be looking at (<a href="http://graphql.org/graphql-js/">GraphQL</a>!).</p><h3>Conclusion</h3><p>Even though you’re low on resources, there should always be time to take on new stuff, even though it means late nights and a lot of Club Mate intake. As you can see in this project we actually totally replaced what we had before and this is now paving out the future structure for all new projects that are built at HelloFresh.</p><p>Of course this version of the new account area is not perfect, I definitely recommend starting small. Very small, smaller than we did at least. If I could go back in time. I would probably just start with one single page, or one single flow. Start so small, that throwing it away entirely, doesn’t hurt.</p><p>There’s definitely still a big road ahead of us, but where is the fun if there wasn’t any. Never stop improving, never stop learning!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=15ec24baacbe" width="1" height="1" alt=""><hr><p><a href="https://engineering.hellofresh.com/javascript-con-carne-15ec24baacbe">JavaScript con Carne</a> was originally published in <a href="https://engineering.hellofresh.com/">HelloTech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>