<?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[Stuart Tech - Medium]]></title>
        <description><![CDATA[Developing last-mile delivery - Medium]]></description>
        <link>https://medium.com/stuart-engineering?source=rss----e5dec0da746d---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Stuart Tech - Medium</title>
            <link>https://medium.com/stuart-engineering?source=rss----e5dec0da746d---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 06 Jun 2026 15:23:07 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/stuart-engineering" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[GraphQL Stubbing Using Cypress]]></title>
            <link>https://medium.com/stuart-engineering/graphql-stubbing-using-cypress-2a214c56dabc?source=rss----e5dec0da746d---4</link>
            <guid isPermaLink="false">https://medium.com/p/2a214c56dabc</guid>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[automation]]></category>
            <category><![CDATA[frontend]]></category>
            <category><![CDATA[cypress]]></category>
            <dc:creator><![CDATA[Victor Vargas]]></dc:creator>
            <pubDate>Tue, 07 Feb 2023 08:47:30 GMT</pubDate>
            <atom:updated>2023-02-07T08:47:30.679Z</atom:updated>
            <content:encoded><![CDATA[<h3>GraphQL stubbing using Cypress</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*v3p5Wt0wb_KgQCakwSrhOg.png" /></figure><p>We in the <strong>Test Engineering team at Stuart</strong> are very excited to share how we use Cypress as a testing framework on all our front-end applications.</p><p>There are several reasons why we want to do this, but these are our top two:</p><ul><li>Today, UI tests are written in Java with Selenium and maintained by QA, which inhibits collaboration with other FE engineers as it’s not their main stack.</li><li>Stubbing network requests/responses are handled with an external mockserver.</li></ul><p>How does Cypress solve these issues?</p><ul><li>We can implement the tests with Typescript, the stack that our FE engineers use daily</li><li>Stubbing network requests/responses are handled internally.</li></ul><p>Even though <strong>team collaboration is the key advantage of this change</strong>, there are other <a href="https://docs.cypress.io/guides/overview/key-differences#Architecture">architectural</a> advantages to Cypress — keep reading as we dig deeper into this stubbing topic:</p><p>In Selenium, test cases use the <a href="https://www.selenium.dev/documentation/webdriver/understanding_the_components/">WebDriver component</a> to communicate with the Browser Driver, which will then interact with the actual browser to execute the commands. Communications between all the components throughout this route are two-way, so that information can seamlessly flow back to the WebDriver from the actual browser. Likewise, developers will need different browser drivers for different types of browsers. Simply stated, Selenium runs outside the browser and executes the commands via the network.</p><p>In contrast, <strong>Cypress executes test cases directly inside the browser</strong>. A server process that powers Cypress makes it possible for Cypress to execute code in the <a href="https://docs.cypress.io/guides/overview/key-differences#Architecture">same run loop as the application</a>. Both Cypress and the server process constantly communicate with each other to perform tasks, enabling Cypress to respond to application events in real-time. This communication also allows Cypress to interact with OS components for tasks outside the browser, such as taking screenshots.</p><p>Cool, right?</p><p>To stub, we use cy.intercept() <a href="https://docs.cypress.io/api/commands/intercept">command</a>.</p><p>As our FE application consumes a GraphQL API, we need to be able to stub it seamlessly. Here we see our first limitation.</p><p>Since GraphQL exposes a single resource with different queries/mutations, stubbing becomes more challenging. We will have to implement a stub for that exact resource.</p><pre>cy.intercept({ method: &quot;POST&quot;, url: &quot;/gql&quot; }, (req) =&gt; {<br>  req.reply({fixture: &quot;aResponseBody.json&quot;,});<br>});</pre><p>The first attempt to stub a different query was to repeat that exact stub but change the response fixture.</p><pre>cy.intercept({ method: &quot;POST&quot;, url: &quot;/gql&quot; }, (req) =&gt; {<br>  req.reply({fixture: &quot;anotherResponseBody.json&quot;,});<br>});</pre><p>However, since the matching criteria {method, url} are the same for requests, the first one will not be considered anymore (as per Cypress implementation, the last stub stays).</p><p>To solve this and to differentiate requests, we can use operationName, which is unique. The previous code will evolve as follows:</p><pre>cy.intercept({ method: &quot;POST&quot;, url: &quot;/gql&quot; }, (req) =&gt; {<br>  if (req.body.operationName.includes(&quot;anOperationName&quot;)) {<br>    req.reply({ fixture: &quot;aResponseBody.json&quot; });<br>  }<br>  if (req.body.operationName.includes(&quot;anotherOperationName&quot;)) {<br>    req.reply({ fixture: &quot;anotherResponseBody&quot; });<br>  }<br>})</pre><p>If we want to deal with only a few requests, this approach is more than valid, but our Dashboard application, for example, produces many requests. This means that simply for logging, the code looks like:</p><pre>cy.intercept({ method: &quot;POST&quot;, url: &quot;/gql&quot; }, (req) =&gt; {<br>  if (req.body.operationName.includes(&quot;GetClient&quot;)) {<br>    req.reply({ fixture: &quot;/gql/GetClient/corporateClient.json&quot; });<br>  }<br>  if (req.body.operationName.includes(&quot;GetZonePackageTypes&quot;)) {<br>    req.reply({ fixture: &quot;/gql/getZonePackageTypes.json&quot; });<br>  }<br>  if (req.body.operationName.includes(&quot;GetPackageSizes&quot;)) {<br>    req.reply({ fixture: &quot;/gql/getPackageSizes.json&quot; });<br>  }<br>  if (req.body.operationName.includes(&quot;GetScheduledSlots&quot;)) {<br>    req.reply({ fixture: &quot;/gql/getScheduledSlots.json&quot; });<br>  }<br>  if (req.body.operationName.includes(&quot;GetTransportTypes&quot;)) {<br>    req.reply({ fixture: &quot;/gql/getTransportTypes.json&quot; });<br>  }<br>  if (req.body.operationName.includes(&quot;GetClosedZones&quot;)) {<br>    req.reply({ fixture: &quot;/gql/getClosedZones.json&quot; });<br>  }<br>  if (req.body.operationName.includes(&quot;GetJobs&quot;)) {<br>    req.reply({ fixture: &quot;/gql/getJobs.json&quot; });<br>  }<br>  if (req.body.operationName.includes(&quot;GetTermsAndConditions&quot;)) {<br>    req.reply({ fixture: &quot;/gql/getTermsAndConditions.json&quot; });<br>  }<br>  if (req.body.operationName.includes(&quot;GetSavedPlaces&quot;)) {<br>    req.reply({ fixture: &quot;/gql/getSavedPlaces.json&quot; });<br>  }<br>  if (req.body.operationName.includes(&quot;GetZones&quot;)) {<br>    req.reply({ fixture: &quot;/gql/getZones.json&quot; });<br>  }<br>  if (req.body.operationName.includes(&quot;getClientTier&quot;)) {<br>    req.reply({ fixture: &quot;/gql/getClientTier.json&quot; });<br>  }<br>})</pre><p>And the problem can (and will) snowball as soon as we want to either add a new stub or modify an existing one (Remember: we will need to execute the stub again with the new value).</p><p><a href="https://docs.cypress.io/api/cypress-api/custom-commands"><strong>Commands</strong></a><strong> to the rescue!</strong></p><p>We’ve have created a command that will store in a key-value pair the operation name with their corresponding responses that have already been registered:</p><pre>const registeredResponses = new Map&lt;string, string&gt;();<br><br>Cypress.Commands.add(<br>  &quot;interceptGql&quot;,<br>  (operationName: string, response: string) =&gt; {<br>    registeredResponses.set(operationName, response);<br>    cy.intercept({ method: &quot;POST&quot;, url: &quot;/gql&quot; }, (req) =&gt; {<br>      if (registeredResponses.has(operationName)) {<br>        req.reply({<br>          fixture: `/gql/${registeredResponses.get(<br>            req.body.operationName<br>          )}.json`,<br>        });<br>      }<br>    });<br>  }<br>);</pre><p>This way, every time we want to add or overwrite an already stubbed response, we don’t need to mock it all at once; it will internally do so.</p><p>Imagine we want to change a stub during the test execution for the package sizes we have. The new implementation would look like this:</p><pre>describe(&quot;/new screen tests&quot;, () =&gt; {<br>  it(&quot;should stub different package sizes seamlessly&quot;, () =&gt; {<br>  cy.interceptGql(&quot;GetPackageSizes&quot;, &quot;getPackageSizes&quot;);<br>  // Do something<br>  // Do something else<br>  // I write some more lines just in case Elon Musk acquires Stuart<br>  // I don&#39;t want to be fired<br>  // And more lines of code means better code<br>  cy.interceptGql(&quot;GetPackageSizes&quot;, &quot;getAnotherZonePackageSizes&quot;);<br>  });<br>});</pre><p>This is how we overcame this limitation. Do you have any other solution that you would like to share? Please share your thoughts by adding your comments!</p><p><em>Looking to join the Stuart team? Take a look at our open roles </em><a href="https://stuart.com/careers/"><em>here</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2a214c56dabc" width="1" height="1" alt=""><hr><p><a href="https://medium.com/stuart-engineering/graphql-stubbing-using-cypress-2a214c56dabc">GraphQL Stubbing Using Cypress</a> was originally published in <a href="https://medium.com/stuart-engineering">Stuart Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Stuart’s Data Journey: How Gustavo and the BI engineering team polished the T in ELT]]></title>
            <link>https://medium.com/stuart-engineering/stuarts-data-journey-how-gustavo-and-the-bi-engineering-team-polished-the-t-in-elt-9f1c17402abe?source=rss----e5dec0da746d---4</link>
            <guid isPermaLink="false">https://medium.com/p/9f1c17402abe</guid>
            <category><![CDATA[airflow]]></category>
            <category><![CDATA[data-engineering]]></category>
            <category><![CDATA[analytics]]></category>
            <category><![CDATA[data]]></category>
            <dc:creator><![CDATA[Gustavo Frigo]]></dc:creator>
            <pubDate>Tue, 31 Jan 2023 08:32:29 GMT</pubDate>
            <atom:updated>2023-02-06T13:50:05.728Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Gustavo Frigo | Data Engineering Lead</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_4URcQ-iMFAhfRb6JA14sQ.png" /></figure><h3>Chapter 1: Early Days</h3><p>It was early 2020, the beginning of the Covid pandemic. As in many European countries, France was locked down except for essential services. That gave me plenty of time to catch up on my favourite series — but a few weeks in, I realised the curve wasn’t getting any flatter!</p><p>Despite the odds, Stuart was growing exceptionally. In fact, our growth was exponential, which introduced a couple of issues, one of them being the question of scaling our capability to act. I was part of a team of just three BI analysts about to face a rather big challenge: How do we quickly empower analysts to generate insights from our data and fast?</p><p>In this upcoming series, I’ll take you through our transformational journey (pun intended) of how we scaled our teams, enabling them to do their (analytical) jobs autonomously without relying on my team and me. I’ll cover this topic in the following chapters:</p><p>Chapter 1: Early Days</p><p>Chapter 2: Drawing Board</p><p>Chapter 3: New Beginnings</p><p><strong>The very start of our journey</strong></p><p>Like many other start-ups, we initially opted for a fleshy open-source data stack. Our team of three had the basics covered; we had a set of pipelines running in Airflow with several custom-made operators that allowed us to ingest data from APIs, do SQL-based transformations, and report on the results. In theory, the stack worked and worked for a while, but as we entered the turbulent growth phase, we — the BI team — became the bottleneck.</p><p><strong>Enter the </strong><a href="https://bigdataldn.com/news/the-modern-data-stack-approach-elt-over-etl/"><strong>modern data stack</strong></a><strong> phase</strong></p><p>We were confident using Redshift as our data warehouse, but we needed the rest of the stack to scale. Quickly, we decomposed our tooling behind the Extract (E), Load (L) and Transform (T) components. For the E &amp; L, we built an in-house Pipeline Framework in combination with Fivetran, which I’ll cover in a separate post. Crucially, however, we started looking for alternatives to implement the T in ELT.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Is1Fr4qbz7LD0KfC4PVkFg.png" /></figure><p><strong>Quickly, </strong><a href="https://www.getdbt.com/"><strong>dbt</strong></a><strong> came out on top as a potential candidate for transforming data.</strong> We saw the huge potential of embedding jinja in SQL and immediately fell in love with its dependency management options. We started trialling the tool as an internal PoC project, and shortly after, we accepted it into our tool stack.</p><p>Stuart kept growing exponentially, and it was the right time to put dbt to the test. We had an ambitious goal of migrating all our legacy pipelines to dbt. This meant not only doing the physical migration of our pipelines but also onboarding new analysts outside our team to dbt. This would enable us to gain <a href="https://medium.com/swlh/the-amazing-flywheel-effect-80a0a21a5ea7">momentum</a> as new models would all be built under the same framework.</p><p>We were already doing development on a few pipelines and had even deployed some processes to production. After going through this process for a few cycles, we realised that there were friction points that would stump the adoption of dbt as a company-wide tool.</p><p><strong>Shoot for the moon. Even if you miss, you’ll land among the stars</strong>.</p><p>As <em>lazy</em> developers, we love to build custom scripts to reduce the cognitive load of remembering commonly used patterns, a.k.a. shortcuts. In our case, we’d built a small collection of <a href="https://www.gnu.org/software/make/manual/html_node/index.html">recipes</a>, which worked well for our usage (local testing, grouping common models, adding runtime parameters, etc.).</p><pre>#!/bin/zsh</pre><pre>#usage: zsh backfill_loop.zsh -s 2021-02-21 -e 2021-03-01 -m dbt_staging.my_great_model &gt; my_great_model.log &amp;<br>while getopts s:e:m: flag<br>do<br>    case &quot;${flag}&quot; in<br>        s) start_date=${OPTARG};;<br>        e) end_date=${OPTARG};;<br>        m) dbt_model=${OPTARG};;<br>    esac<br>done</pre><pre>d=$start_date<br>while [[ $d &lt; $end_date ]]; do<br>    echo $d<br>    dbt run --target prod --models $dbt_model --vars &#39;{&quot;start_date&quot;: &#39;$d&#39;}&#39;<br>    d=$(date -j -v +1d -f &quot;%Y-%m-%d&quot; &quot;$d&quot; +%Y-%m-%d)<br>done</pre><p><em>Example of one of our scripts for backfilling models one day at a time (yes, some models aren’t able to fast forward to the latest date, but that will be the discussion of a later post…)</em></p><p>On that same note, we set out to build a custom script that would package these and other common patterns and give a unified user interface to other users in the company. We spent a good few weeks working out the kinks and were set to showcase this to a selected subset of analysts. The big day came. We had two days to conduct a workshop introducing dbt to our beta users. We had our pot of coffee and started onboarding users — only to face a setback: the challenge of local machines.</p><ol><li>Most of our analysts work on Apple machines, and no two analysts have the same setup.</li><li>Out of all the installations we performed, three-quarters went without a blip.</li><li>About 20% needed hands-on support to manage different settings (virtual environments, docker versions, shell differences, etc.).</li><li>About 5% had hard-to-resolve issues (including one user with a different hardware architecture than the rest).</li></ol><p>In the next chapter, I’ll cover the lesson learnt and how it inspired us to go back to the drawing board.</p><p>Stay Tuned!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9f1c17402abe" width="1" height="1" alt=""><hr><p><a href="https://medium.com/stuart-engineering/stuarts-data-journey-how-gustavo-and-the-bi-engineering-team-polished-the-t-in-elt-9f1c17402abe">Stuart’s Data Journey: How Gustavo and the BI engineering team polished the T in ELT</a> was originally published in <a href="https://medium.com/stuart-engineering">Stuart Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to mock an SSO Keycloak React app for Cypress testing]]></title>
            <link>https://medium.com/stuart-engineering/how-to-mock-an-sso-keycloak-react-app-for-cypress-testing-c80ba38a13d1?source=rss----e5dec0da746d---4</link>
            <guid isPermaLink="false">https://medium.com/p/c80ba38a13d1</guid>
            <category><![CDATA[mock]]></category>
            <category><![CDATA[keycloak]]></category>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[cypress]]></category>
            <dc:creator><![CDATA[Jordi Girós Guerrero]]></dc:creator>
            <pubDate>Mon, 21 Mar 2022 09:16:24 GMT</pubDate>
            <atom:updated>2022-03-21T11:50:21.077Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/950/1*04D8gDXfnOvCqjjLqNPIBQ.jpeg" /></figure><h3>Initial idea</h3><p>It’s been a long time since we started working on the creation of an app for our clients that will give them a huge improvement on some of the management systems we use in Stuart.</p><p>Our frontend team was in the front line of all these changes and they decided to start a new project creating a specific repository for this development. They focused their efforts on creating an application based on React that was capable of managing a Single Sign On (SSO) system of login giving the users access to multiple tools inside. In our case, the SSO system was a Keycloak instance. Each of these tools has its own different permissions based on the client. So, depending on the permissions a user has on the SSO system, they will be able to see some tools or others. And this concept is key to understanding the problem we faced.</p><p>Since different users had access to different tools inside the app, the traditional approach of having one token to authenticate against the API was a liability as this would give the users access to graphQL queries and mutations that they should not be able to access. To go around this, each tool was set up to be an OAuth client requiring users to generate a new token as they navigated between tools. The Frontend server handled this by matching the current path the user was on to a tool and then redirecting to Keycloak with the appropriate client ID. Keycloak redirected the user back to the application with an authentication code which was then used in combination with the client secret to request access and refresh token.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/485/0*waIVidbADj1v_cQW" /></figure><p>The idea to start using Cypress was discussed for a long time between the QA and FE teams. This new application required mocking graphQL API calls and converting with E2E tests a lot of dynamic changes on the interface that seemed perfect to introduce it.</p><h3>The problems we found</h3><p>The first important step after the installation was related to our SSO login system. Any test on the application should bypass this. We were interested in testing the login in isolation, but we don’t want to perform the login on each test as it will be very time-consuming. After some research, we found a possible valid solution by using a combination of approaches.</p><p>We found a third-party library created specifically for mocking Keycloak called <a href="https://www.npmjs.com/package/cypress-keycloak-commands">cypress-keycloak-commands</a>. It’s a simple and easy-to-use tool that perfectly does what it was made for. We also needed to define a cookie with our client id and secret credentials and we set it directly using Cypress due to our technical requirements.</p><p>It was nice to run the first test after implementing this approach. It worked! The login system was bypassed and we entered the main menu of our project. But… Here we faced THE PROBLEM.</p><p>After logging in with the mock generated by the library and reaching the internal list of available tools, we found that we were unable to mock the second permissions asking the SSO using that library because of what we told about how our app works. Our Cypress server couldn’t access our FE Server which was the one doing the request asking for permissions on the SSO, so there was no possible mock for this. Neither directly in Cypress nor using the external library.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/467/0*afvHSmpximT-rYwn" /></figure><h3>The real solution. Frontend team to the rescue!</h3><p>It became very clear that we needed to somehow mock the SSO authentication flow on localhost but we also did not want to change any production code to cater for tests. Pointing the application to a local server instead of Keycloak and getting a mock token would not be a problem as all Keycloak URLs were set as environment variables and all data would be mocked during tests.</p><p>In the end, the solution was inspired by how our development environment is set up. During development mode there are two express servers running, one is the same server that runs on production and the other one handles hot reloading and serving assets.</p><p>After some brainstorming, it was decided to create a similar script to start up two express servers for cypress. One server handles server-side rendering and serves the app the same as production and the other one has two routes added to it. One route handled redirecting back to the main server with a mock authentication code and the other route responded with a mock access and refresh token.</p><pre>app.get(&quot;/openid-connect/auth&quot;, (req, res) =&gt; {<br>  const { redirect_uri: redirectUri, state } = req.query;<br><br>  res.redirect(<br>    `${redirectUri}?session_state=${sessionState}&amp;code=${code}&amp;state=${state}`<br>  );<br>});<br><br>app.post(&quot;/openid-connect/token&quot;, (req, res) =&gt; {<br>  res.status(200).json(tokenPayload);<br>});</pre><h3>Final test</h3><p>With those changes in mind, we just needed to adapt our CI scripts on Jenkins so the frontend server app was configured correctly to run Cypress tests as explained. And it all started working like a charm!</p><p>After creating some tests, we were able to login into the app and see all the tools listed but we were also capable of accessing any tool and, using the <em>intercept </em>method and some fixtures for mocking, we just got the information that we wanted to test!</p><p>Special thanks to Tonio Buttigieg for all the time dedicated to the mocking system and writing this article.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c80ba38a13d1" width="1" height="1" alt=""><hr><p><a href="https://medium.com/stuart-engineering/how-to-mock-an-sso-keycloak-react-app-for-cypress-testing-c80ba38a13d1">How to mock an SSO Keycloak React app for Cypress testing</a> was originally published in <a href="https://medium.com/stuart-engineering">Stuart Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How we’re building our data platform as a product]]></title>
            <link>https://medium.com/stuart-engineering/how-were-building-our-data-platform-as-a-product-f89142b6547f?source=rss----e5dec0da746d---4</link>
            <guid isPermaLink="false">https://medium.com/p/f89142b6547f</guid>
            <category><![CDATA[data-platforms]]></category>
            <category><![CDATA[product-data]]></category>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[product-management]]></category>
            <category><![CDATA[data-product-manager]]></category>
            <dc:creator><![CDATA[Osian Llwyd Jones]]></dc:creator>
            <pubDate>Thu, 18 Nov 2021 12:47:24 GMT</pubDate>
            <atom:updated>2024-12-20T21:37:48.750Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*k-nalHHhV4NXMiGDgdPMbg.png" /></figure><p><em>In this article I share the experience of my first three months at Stuart as a Data Product Manager for our internal data platform; what it means to sit at this powerful intersection between data and product; and why it’s critical for a modern data platform team like ours to “think product” in truly delivering business value. I’ll draw on the challenges faced when looking at a data platform through a product lens, redefining it from simply a “tech stack” to what it really is: a product that delivers value to hundreds of business users day in day out. My hope is that this will serve as a guide to data platform teams at any stage of becoming more product-led while contributing to the emerging space of data products more generally.</em></p><h3><strong>The uncomfortable questions</strong></h3><p>Over recent years most companies have adopted a data platform as a way to manage how data is ingested, stored, transformed and accessed at scale. The end goal is typically to support the business in making fast and reliable data-driven decisions<em>, </em>and here at Stuart we’re no different.</p><p>In the face of exploding volume and complexity of data which needs no further explanation, the focus of data platform teams have understandably been concentrated on stability and reliability at scale. For example, ensuring the smooth running of business-critical pipelines, minimising downtime or optimising query efficiency.</p><p>While companies large and small have made considerable gains in building a scalable and sustainable architecture, we’re left with the uncomfortable questions: is what we’re doing truly providing value? Do we really know who our users are and understand their needs? If so, can they generate insights in a fast and reliable way? As long as users don’t complain and pipelines don’t fail, does that mean all is well? For all our investment in data, are we seeing the return?</p><p>These questions that touch on the concept of value are particularly difficult for an internal platform that doesn’t sell to or make money from our users. Moreover, the added <a href="https://ericdataproduct.substack.com/p/why-is-treating-data-like-a-product">complexity of quantifying the value that data brings</a> forces us to consider alternative definitions of value:</p><ul><li><em>Top-down</em>: How does the data platform align with and contribute to company goals?</li><li><em>Bottom-up</em>: Would users choose to use the data platform, and is it easy to use?</li></ul><p>While the importance of the former cannot be underestimated and should play a critical part in goal setting and prioritisation, this is often a question of modifying <em>process</em>. Meanwhile, the latter is a more complex topic, and especially so for a data platform intended for business (i.e. human) decision-making. To better understand users requires more than a change in process — we need a change of <em>mindset</em>.</p><p>When the data platform is viewed purely from a technical lens, we put ourselves at risk of forgetting our users and their needs, building solutions before we’ve understood the problem and further increasing the data/value gap.</p><p>These questions are particularly important to ask now, when the growing discussion around <a href="https://martinfowler.com/articles/data-monolith-to-mesh.html"><em>data mesh</em></a> suggests we’re on the verge of a radical shift in how business interacts with and governs data. This only further increases the need to proactively and continuously understand our users and their evolving needs.</p><h3><strong>The data platform: More than a tech stack</strong></h3><p>Much in the same way that experimentation platforms, algorithms, and even the data itself are being increasingly considered products, we turn our attention to the data platform. For simplicity, consider the data platform to refer to all the tech and tools that collectively serve the business to make data-driven decisions.</p><p>If the previous section wasn’t enough to convince us, there is one concept that helps sum up the need for product thinking for a data platform more than anything:</p><blockquote><em>Accessing and working with data is a user experience.</em></blockquote><p>In the same way we use applications for almost everything in our personal lives from measuring our heart rates to finding a new house, the experience of accessing data to make decisions should be no different. But the concept of UX is rarely heard among data teams. This is hardly surprising if the data platform is viewed entirely as a technical problem and resourced accordingly; but perhaps added to this is the idea that as user-facing data tools are frequently bought and often come with a UI, that these UIs automatically “take care” of our user needs, i.e our job is done.</p><p>But a good UI cannot replace the need for a good UX. To think that in most modern organisations, hundreds or even thousands of users are interacting with data on a daily basis, the idea of <em>not</em> continuously investing in discovering users’ needs, pain points and desires, of <em>not</em> using this user context to shape our roadmap for success, in other words, <em>not</em> thinking of our data platform as a product should concern anyone investing in it in the first place.</p><p>Here at Stuart the Data team is fortunate to sit alongside a strong Product team whose philosophy is centred on continuous discovery: identifying opportunities in the form of user needs, pain points and desires and using this to guide our vision, strategy and goals. This means that as well as building products that make sense both from a technical standpoint and aligned with company goals, we’re also strongly led by our users. This approach means we’re more likely to deliver a data platform that users love, and less likely to jump into solutions that ultimately only we in the platform teams love.</p><h3><strong>Data Platform as a Product: First Principles</strong></h3><p>Until now we’ve mostly discussed <em>the why</em>. It’s now time to move from theory to reality and put this into practice: <em>the how</em>. The complex make-up of the data platform doesn’t make it easy to define as a product by any means. Here we present our “first principles” approach to this, which focused on three broad areas:</p><ul><li>First, at the end of almost all of our data efforts is a user experience. But users are not one and the same; they are diverse and evolving, fast. For this reason we should start by considering our different <strong>user</strong> <strong>personas </strong><em>(“who are our users?”).</em></li><li>Second, we also need to consider the motives and reasons behind needing a data platform in the first place. We should at least give consideration to <strong>user intents </strong><em>(“what do our users want to do with data?”)</em>, broadly similar to use cases but at a much higher level.</li><li>Finally, for the multifaceted data platform we need to make life easy for both our users and ourselves by breaking it down into easily identifiable <strong>product</strong> <strong>components </strong><em>(“how do we present our data platform to our users?”)</em><strong>. </strong>These components are accessed by users through<strong> interfaces</strong>, typically representing third party or internal tools.</li></ul><p>Together with other data leads and interviews with key business stakeholders we investigated each of these areas one by one. To ensure it didn’t become a one-off exercise we made them as visible as possible internally, e.g as part of onboarding a new Data or BI Engineer. Doing this also incentivises us to keep them relevant and up to date.</p><h3><strong>User Personas</strong></h3><p>If we want to shift to a more user-centric approach, understanding who our different users are feels like a logical starting point.</p><h4><em>Approach</em></h4><ul><li>If our goal is to provide constant reminders to our data platform teams that there’s a user at the end of our efforts, we need to reflect reality as much as possible. We used names, photos and real quotes from users.</li><li>We avoided creating personas directly along job titles or business departments, as it often obfuscates the nuances between teams and is less flexible to change. e.g. a data analyst in one team could have a very different profile to a data analyst in another.</li><li>We tried to at least get a rough number of the size of each persona through looking at usage logs etc. This helps give more meaning and context to a persona.</li></ul><h4><em>Outcome</em></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XIqI9F6SQ3-0NpuSLhAAGg.png" /><figcaption><em>Our data platform user personas</em></figcaption></figure><h3><strong>User Intents</strong></h3><p>Why would anyone make use of a data platform in the first place? What are the key actions our combined user personas should expect to be able to perform autonomously? These were the questions we asked during this step.</p><h4><em>Approach</em></h4><ul><li>We placed the word <em>autonomously</em> front and centre of this discussion. As we scale we must strive to reduce the dependency on the data platform teams so users can act fast, albeit in an aligned and secure manner.</li><li>With this in mind, we took an aspirational approach in considering both what the user can and cannot do with data today by themselves.</li><li>We avoided listing all the specific use cases at this early stage due to the sheer volume of these, which could easily run into the several hundreds. Instead, we focused on summarising high-level intents as described by a single action (verb). As we mature and evolve we can add (and remove) nodes to this “tree” of user intents, possibly then going down to specific use cases.</li><li>As soon as broad categories of user needs emerged we began to group these. For example, the “need right now” vs. “need for future”. The first represents ad-hoc needs for data answers immediately; while the latter represents intents to build or automate in order to avoid repetition in future.</li></ul><h4><em>Outcome</em></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*goMpfeN16_BsiIJaN3gAmA.png" /><figcaption><em>Our data platform user intents, summarising the key actions our combined user personas expect to be able to perform autonomously</em></figcaption></figure><h3><strong>Product Components</strong></h3><p>With a better overview of the users and motives for using a data platform, we turn our focus inwards and ask what our data platform offers. Similar to how market-facing products sit on a shelf or under the “Our Products” dropdown on a website, how do we represent our data platform as a portfolio of products?</p><h4><em>Approach</em></h4><ul><li>We only considered business user-facing aspects of the platform as product components. The goal here is for users to get an easy answer to “what’s in it for me?”</li><li>We divided the platform into components that make sense to our combined user personas. This is an important point, because the purpose of this exercise is <strong>not</strong> to end with another technical architecture diagram. Components should be named in a way that clearly communicates their purpose.</li><li>Just as for user intents, we shouldn’t be bound only by the current or “live” platform components. Even if there are some that are planned or still under consideration there is no reason not to show them among the components.</li></ul><h4><em>Outcome</em></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*P9V6XPlnAcAK6Mg7tE6Pkw.png" /><figcaption><em>Our data platform as a portfolio of user-facing products. No technical architecture diagrams were harmed in the making.</em></figcaption></figure><p>One of the advantages of dividing our platform in this way is that we can build a set of KPIs for each product component, which would then form the basis of future OKRs:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9vFVi9fSh-hYqtPMzXON7w.png" /><figcaption><em>Components help us to treat, and therefore measure, the various parts of the platform differently</em></figcaption></figure><h4><strong>A Note about Interfaces</strong></h4><p>At a later stage, we mapped the more familiar user interfaces (usually third party tools) to each component, so that users can clearly navigate to the specific tool(s) for accessing each component. We felt it important to distinguish between components and interfaces for these reasons:</p><ul><li>Interfaces change. If we were to replace our primary visualisation interface from Tool A to Tool B, the interface has changed but the component remains unchanged.</li><li>Interfaces are increasingly multi-purpose. Many third party tools are expanding beyond their core offering and some even considered platforms in themselves. This means their purpose isn’t always obvious to users; components help to clarify this. This also means there isn’t a one-to-one mapping of interface to component. For this reason, we introduce the idea of primary and secondary interfaces. The primary interface is where the user should typically be directed. Failing that, the secondary interface may be partially able to serve as that product component.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/974/1*pq91sQPWfIIO3SZClcr8ew.png" /><figcaption><em>Mapping the interfaces to our product components helps users navigate the data platform between its various technologies and tools</em></figcaption></figure><h3><strong>Joining the dots</strong></h3><p>By taking a step back and linking the use cases, user personas, components this enables us to:</p><ul><li>Look at our data platform as a manageable set of product components we can measure more easily and build OKRs around.</li><li>Identify gaps in our offering, where important user intents are not linked to a live product component. This is particularly useful as a starting point for building a longer-term strategy.</li><li>Decide how we best organise our teams around common KPIs.</li><li>How we structure our roadmap and communications to the business on what we’re delivering, to who and why.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uAwTms1HcyGvi807xEZx1w.png" /><figcaption><em>Our data platform: still a tech stack, but also a product that delivers value to hundreds of different users for different motives, every day</em></figcaption></figure><h3><strong>Where we’re heading next</strong></h3><p>We’ve introduced the concept of a data platform as a product, moving beyond simply viewing it as a tech stack. We did this by identifying who our users are, their intents and what our product offering is that can meet those needs.</p><p>As we’ve witnessed at Stuart most modern data platforms are complex and multifaceted, so the simple act of putting these on paper and visualising our platform as a collection of products that deliver value to our users is already a meaningful first step.</p><p>Our work is by no means done here — we are only at the start of our journey to becoming more product-led. These practices and principles will form a solid foundation for what comes next: discovering opportunities on an ongoing basis. Opportunities that will play a key role in building our strategy and OKRs for the coming year, becoming a data platform that continuously brings value to our users and to our business.</p><p><em>Main reading sources:</em></p><ul><li>Continuous Discovery Habits, Teresa Torres</li><li>Inspired: How to Create Tech Products Customers Love, Marty Cagan</li><li>How to Move Beyond a Monolithic Data Lake to a Distributed Data Mesh, Zhamak Dehghani</li><li>How to Build your Data Platform like a Product, Barr Moses</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f89142b6547f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/stuart-engineering/how-were-building-our-data-platform-as-a-product-f89142b6547f">How we’re building our data platform as a product</a> was originally published in <a href="https://medium.com/stuart-engineering">Stuart Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating and developing a high performance Agile team — Part 2]]></title>
            <link>https://medium.com/stuart-engineering/creating-and-developing-a-high-performance-agile-team-part-2-4b7052d7bf6a?source=rss----e5dec0da746d---4</link>
            <guid isPermaLink="false">https://medium.com/p/4b7052d7bf6a</guid>
            <category><![CDATA[agile]]></category>
            <category><![CDATA[high-performing-team]]></category>
            <category><![CDATA[scrum]]></category>
            <category><![CDATA[kanban]]></category>
            <category><![CDATA[agile-coach]]></category>
            <dc:creator><![CDATA[L Boni]]></dc:creator>
            <pubDate>Tue, 26 Oct 2021 13:32:36 GMT</pubDate>
            <atom:updated>2021-10-26T13:35:18.863Z</atom:updated>
            <content:encoded><![CDATA[<h3><strong>Creating and developing a high performance Agile team — Part 2</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RAr3XAQYZQvumYGNxLh0PQ.png" /></figure><p><strong>Discover the strategies of Stuart’s Head of Agile for developing high-performing teams.</strong></p><p>The topic of High Performance Agile teams is one that comes up frequently in conversations in Agile circles. There is a lot of confusion around it, I think in part because it’s difficult to come to an agreement of how we define a high performing team and what the benefits are. At Stuart we recently had a discussion around High Performing Agile teams in our Agile Community of Practice (CoP). We wanted to gain a common understanding of what we mean by it, what are the benefits of high performing teams, what are some patterns and anti-patterns, and most importantly, what are some tips and tricks for developing high performing teams. So, we put our heads together and came up with some thoughts that I am now sharing as part of this blog post.</p><p><em>This is the second article in a series about creating and developing a high performance Agile team. If you missed Part 1, </em><a href="https://medium.com/stuart-engineering/creating-and-developing-high-performance-agile-team-part-1-the-benefits-of-high-performance-24495db82951"><em>check it out here</em></a><em>.</em></p><h3><strong>How can you help your team develop through the Tuckman Model?</strong></h3><h4><strong>7 tips and tricks to help teams progress from one stage to the next.</strong></h4><p>Set a clear purpose and mission for the team and revisit it throughout the process. It’s important to have the answers to the following questions and make sure they are clearly understood by the team.</p><ul><li>Why does the team exist?</li><li>Who are the personas that represent the customers of the team?</li><li>Who are the stakeholders and business sponsors and what are their expectations?</li><li>What values matter to the stakeholders and to the team?</li><li>What problem(s) will you solve and why do you need to solve it?</li><li>What opportunities are you trying to capture?</li><li>What value will the team bring to the company?</li></ul><p>Set ground rules and make sure they are followed. For this we found it useful to establish working agreements with the team.</p><ul><li>What behavior is acceptable? What is not?</li><li>How will the team interact?</li><li>How often will the team meet?</li><li>What tools will the team use?</li><li>How will the team organise and remain open and transparent?</li></ul><p>Share leadership across the team and create an environment where everyone is empowered to lead.</p><ul><li>Everyone on the team should be empowered to lead the team, whether it be through a process, a discussion to solve a problem, or an Agile Ceremony such as backlog refinement or a cycle review.</li><li>Every team should have a facilitator for their Agile Ceremonies but (and this is important) it doesn’t need to be the same person all the time. In fact, it SHOULDN’T be the same person every time.</li><li>Encourage people to be Servant Leaders and coach and guide them to become one.</li><li>Choose the most qualified person to lead a particular discussion or exercise.</li><li>People should be accountable for their solutions, which means they need to be empowered to identify and own them.</li></ul><p>Don’t try to avoid conflicts. They are normal and can be healthy.</p><ul><li>The benefit of working in a team is that you have access to diverse experiences, skills and opinions that aren’t possible alone.</li><li>When members disagree about something, listen to each side. But don’t take one. Search for common ground. Disagreements are often a result of people having different perspectives and the best solution is one that combines multiple perspectives.</li><li>When conflicts are resolved correctly and in a positive way, it can improve team dynamics and bring the team closer together.</li></ul><p>Coach team members on how to become active listeners. Active listening is a technique that keeps you engaged with your team in a positive way. It allows the speaker to be heard and keeps the listener open to what is being said.</p><ul><li>Each person in your group holds some value, otherwise they wouldn’t be there, right? Remind your team to listen and to value each person’s insight.</li><li>Create a safe environment that is open and non-judgmental to encourage everyone to voice their opinion.</li><li>Encourage team members to actively seek the opinions of fellow colleagues on the team from different disciplines. Getting their perspective could prove invaluable.</li></ul><p>Be open to change and to feedback–in other words, strive to continuously improve.</p><ul><li>Encourage the team to talk about what they are doing well. What do they need to improve?</li><li>Provide a space where the team can retrospect on how they are doing and identify things they can improve on. Commit to those things as a team.</li><li>Do not blame individuals for the shortcomings of a team. Work with the team on making things better.</li><li>Accept that the team will make mistakes, celebrate them, learn from them and make improvements based on them.</li><li>Identify solutions for the improvement opportunities identified by the team. Do not turn your retrospective into a meeting where everyone complains and nothing positive comes out of it.</li><li>Tell teams what they are doing right as well as what they need to improve.</li></ul><p>There is no “I” in “team”; everyone contributes to the team’s success.</p><ul><li>Everyone plays a part and contributes to the team’s success.</li><li>When one person on the team is struggling, the entire team struggles. We should work to support each other.</li><li>It is important to instill this sense of responsibility in the team.</li><li>Make sure the team has the right tools to collaborate and that they are being used correctly.</li><li>If you are blocked by someone else’s work, instead of waiting for them to finish so you can start your work, try collaborating with them to complete the work faster so you are working on a common goal.</li></ul><p>Why are team dynamics so important? Why should you care about them? The answer is that it’s because team dynamics directly impact team performance. Think about it for a minute. Intrinsically, we know it’s true. It’s true in almost any industry. In sports, for example, there is a lot of information out there about how team dynamics contribute to their success…. or failure. An Athlete Assessment’s article entitled <a href="https://www.athleteassessments.com/sports-team-chemistry-team-dynamics/">Sports Team Chemistry and Dynamics in Sport</a> talks about this. Team dynamics also play a significant role in business performance of course. Katie Popp talks about this in her article, <a href="https://www.missioncapital.org/blog/insights-and-ideas/blog/understanding-team-dynamics">Understanding Your Team’s Dynamics</a> on the<a href="https://www.missioncapital.org/"> Mission Capital’s website</a>. She also provides a graphical representation of the impact that Tuckman Model phases have on performance (see below).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*LrqixDrp1YN1eoFQ" /></figure><p>What is particularly interesting to me is that there is a lot of overlap between Logistics and Team Dynamics. Both, for example, speak about setting a clear purpose and vision, using the right tools, team alignment and working agreements, embracing change and conflict. I believe that, as Agile practitioners, we bring the most value to a team by helping to build an environment for success. It’s not by telling them what to do or how to do it. It’s about creating an atmosphere where the team comes together to identify and define their own solutions. Focusing on the areas where team logistics and dynamics intersect is an extremely powerful strategy. It allows us, as Agile practitioners, to work on improving both the operational and interpersonal aspects of the team at the same time.</p><h3><strong>Do’s and don’ts of developing a high-performing team</strong></h3><p>As part of our discussion in the Agile Community of Practice at Stuart, we discussed some practical tips on what to do and what not to do to develop high-performing teams. We identified six categories that we thought were especially relevant to developing teams. Below is the output of our conversation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/525/0*8AfePdcFs3GE8-VL" /></figure><h3><strong>Our Stuart values</strong></h3><p>At Stuart we have a <a href="https://stuart.com/blog/culture/our-company-values/">set of values</a> we strive to embody and consciously develop within the organisation. These are values that bind us, bring us closer together and provide a north star to members of the organisation. Developing high-performing teams directly supports several of our internal values, including “Build by empowerment”, “Start with humility”, “Share through cooperation”, and “Solve the problem, right”.</p><p>My hope is that this blog article has given you some insight and food for thought, and that you have some practical knowledge and tools to help you develop high-performing teams. Whether your team is an Agile team or not, it doesn’t matter. Every team and every organisation benefits from high-performing, self-organised and empowered teams!</p><p><em>Like what you see? We’re hiring! </em>🚀<em> Check out our </em><a href="https://stuart.com/careers/"><em>open positions</em></a><em> here!</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4b7052d7bf6a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/stuart-engineering/creating-and-developing-a-high-performance-agile-team-part-2-4b7052d7bf6a">Creating and developing a high performance Agile team — Part 2</a> was originally published in <a href="https://medium.com/stuart-engineering">Stuart Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating and Developing High Performance Agile Team — Part 1: The benefits of High Performance…]]></title>
            <link>https://medium.com/stuart-engineering/creating-and-developing-high-performance-agile-team-part-1-the-benefits-of-high-performance-24495db82951?source=rss----e5dec0da746d---4</link>
            <guid isPermaLink="false">https://medium.com/p/24495db82951</guid>
            <category><![CDATA[high-performing-team]]></category>
            <category><![CDATA[agile]]></category>
            <category><![CDATA[agile-coach]]></category>
            <category><![CDATA[scrum]]></category>
            <category><![CDATA[kanban]]></category>
            <dc:creator><![CDATA[L Boni]]></dc:creator>
            <pubDate>Tue, 05 Oct 2021 15:47:54 GMT</pubDate>
            <atom:updated>2021-10-05T15:47:52.736Z</atom:updated>
            <content:encoded><![CDATA[<h3>Creating and Developing High Performance Agile Team — Part 1</h3><p><strong>Discover the benefits of a high performance Agile Team and learn to recognise teams that achieve their goals and grow together.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WM_WNByIpXp_lQ3dHgUYzA.png" /></figure><p>The topic of High Performance Agile teams is one that comes up frequently in conversations in Agile circles. There is a lot of confusion around it, I think in part because it’s difficult to come to an agreement of how we define a high performing team and what the benefits are. At Stuart we recently had a discussion around High Performing Agile teams in our Agile Community of Practice (CoP). We wanted to gain a common understanding of what we mean by it, what are the benefits of high performing teams, what are some patterns and anti-patterns, and most importantly, what are some tips and tricks for developing high performing teams. So, we put our heads together and came up with some thoughts that I am now sharing as part of this blog post.</p><p>Before we begin, it is important to note that developing a high performing agile team is a never ending journey and that it is not necessarily linear. A team may become more or less mature for a number of reasons such as a change in scope or vision, changing, adding or removing team members, and organisational restructuring. Below is a summary of our conversation blended together with my own thoughts and experiences. I also included some research I conducted online that supports the need for high performing teams and highlights the struggles to get there. Let’s start by asking ourselves: why do we want high performing agile teams anyway?</p><h3><strong>Why do we want them?</strong></h3><p>High performing teams bring many benefits to the organisation, including:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/457/0*QSX3Jv1ItMN-4kUR" /></figure><ul><li>Better quality solutions</li><li>Sustainable development</li><li>More engaged and empowered team members</li><li>Removing leadership bottlenecks</li><li>Happier teams which are more productive and result in people staying with a company longer</li><li>Better communication</li><li>Trust</li><li>Efficiency</li></ul><h3><strong>How do we define them?</strong></h3><p>We started our conversation in the CoP by defining what we mean by high performing Agile teams. Are we talking about lines of code? Number of releases? Business or customer value? Velocity in the case of Scrum or cycle time in the case of Kanban? The answer is more nuanced than it would appear at first glance. This is because different teams define success in different ways and we need to be careful not to track vanity metrics such as lines of code or velocity. These types of metrics do not speak to the value that the team is delivering. Business value and customer value are great indicators of success but only if you have a way to define and measure them. Many organisations do not. How did we define a high performing Agile team then, you ask? Well, we stuck to the most basic definition possible. First we aligned on the definition of an Agile Team, and then identified what makes it a high performing one.</p><p>An Agile Team is a team composed of cross functional, fully autonomous individuals with the skills necessary to effectively accomplish the work in their backlog. Everyone in the team is empowered to make their own decisions and is held accountable for them. (This is a fairly standard definition of an Agile Team and can be found by doing a quick Google search.)</p><p>A High Performing Agile Team: Is an Agile team made up of highly motivated individuals that consistently delivers exceptional results (however you define them) over a period of time, regardless of the challenges and uncertainty the team may encounter.</p><p>High Performing Agile teams don’t just spring out of nowhere. It takes dedication and leadership at all levels. A lot of effort goes into developing such a team. The results of the team are often due to the sustained efforts of committed people who embody <a href="https://stuart.com/blog/tech/servant-leader/">Servant Leadership</a> values and principles. As I stated earlier, it is an infinite game. High performing teams are rarely satisfied with their performance even if they are performing well. They are constantly looking for ways to do better.</p><p>That being said, the definition of a high performing team can be somewhat vague and general so we continued our conversation by identifying first what is NOT a high performing team and where teams struggle. Secondly, we thought about what we can do as Agile Practitioners to move the team past those struggles to achieve high performance.</p><h3><strong>Why do Teams Struggle to Gain High Performance?</strong></h3><p>There are many reasons why a team might struggle to achieve high performance. I find it useful to break it down into two main categories: Logistics and Team Dynamics. As Agile Coaches our role should be to ensure that every team has an environment that contributes to and is conducive to their success. We should help every team to discover their own solutions without dictating to them what they should be. That is not to say we cannot suggest possible options to them based on our experience working with other teams. Our expertise is why we are Agile Coaches, after all. However it’s important that we approach each team individually. Every team has a set of unique characteristics, team dynamics and struggles. Understanding and recognising them is the key to their success.</p><h4><strong>Team Logistics</strong></h4><p>Let’s talk about the first category I mentioned, Logistics. Logistics is about processes, tools and vision. It’s about how the team accomplishes their goals. For example, does the team have a common vision? Do they have a roadmap? How was that roadmap created? Do they understand their purpose? Do they know how to use the tools at their disposal and are they using them effectively? Are they using the right tools for the task at hand? Does their process work for them or are they working for the process? There is nothing worse than a team following a process that works against their success. This is the fastest way to decrease a team’s happiness and productivity and it will result in sub-optimal solutions. People won’t want to be a part of the team and could potentially leave it.</p><p>The below Matrix is taken — and slightly altered — from a Blog post created by Uday Varma in an article titled<a href="https://www.agileconnection.com/article/8-keys-transforming-high-performance-agile-team"> 8 Keys to Transforming into a High-Performance Agile Team</a> in the<a href="https://www.agileconnection.com/"> Agile Connection</a> website, which is an incredibly useful resource for Agile Coaches and Practitioners. I find it extremely valuable when discussing possible reasons why Agile teams fail to reach high performance and I reference it often. I love being able to use materials that are already out there and created by other Agile Practitioners to help us in our quest for continuous improvements and learning. If anyone finds anything I write useful, it would bring me immense pleasure to know it is being referenced and used to help others in their Agile Evolution. In any case, I want to give a shoutout to Uday!</p><p>We used Uday’s matrix as a starting point. We then added the last column on the right as part of our discussion about potential strategies for reaching high performance, and we captured it in a confluence page for everyone in the organisation to make use of. I should point out that there are other reasons a team may fail to reach logistical high performance. If you are an Agile Coach for such a team, work with them to identify the reason, its root cause, and strategies to address it. Below is the matrix that we, at Stuart, have put together by combining Uday’s original Matrix with the strategies we identified to help us resolve them.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*YDsqtWrbKiQyZnd-" /></figure><p>These aren’t the only areas where a team might struggle logistically. They are the areas we spoke about in our Community of Practice. Your team may share some of these struggles and may have some that are unique to your circumstance. Even in our community there were teams that associated more strongly with some of these areas than others. That’s ok. My point here is not to iterate every single area where a team might struggle but to make the point that Logistical discipline is one of the aspects that is key to your team’s success.</p><h4><strong>Team Dynamics</strong></h4><p>Let’s talk about the other category I identified, Team Dynamics. This is where it gets tricky because every team dynamic is different. To create outstanding Team Dynamics,<a href="https://medium.com/stuart-engineering/servant-leaders-the-cornerstones-of-an-agile-organization-3647acdfb66f"> Servant Leadership</a>, influence management and social engineering all come together for the greater good of the team. An Agile Coach, or anyone in an Agile team, needs to be aware of the dynamics within the team and needs to have an understanding of how to help the team dynamics evolve. There are many lines of thought around this, from things like learning frameworks like Shu Ha Ri to different leadership styles. Regardless of how you approach it, if you understand team dynamics, then it almost doesn’t matter what kind of leadership style you employ (except for command and control, we don’t like command and control). To understand team dynamics I use the Tuckman model. I find it useful and easy to follow and it is universally relevant. Every team, in my experience, goes through these stages. It doesn’t matter if they are an Engineering team, HR, Finance, Operations, or an Agile team. EVERY team goes through this process. It is also important to point out that teams can progress or regress through the model depending on the circumstance. So…. What is it?</p><p>The Tuckman model describes the stages a team goes through in its journey to high performance. There are five stages. Each describes the team’s overall characteristics at that point in time to give insight into where and why a team might struggle. In our conversation with the Community of Practice, we discussed the model and identified some strategies for how to develop a team into the next stage. Again, these are just examples, you may come up with more strategies for your specific team. Additionally, teams may progress through some phases faster than others. The key is to have patience and understand that a team is made up of individuals, each with their own needs and desires.</p><h4><strong>The Five Phases</strong></h4><p>The image below is taken from<a href="https://toggl.com/track/stages-of-team-development/"> Toggl Track: 5 Stages of Team Development</a> and I feel it succinctly and elegantly describes the five stages.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/710/0*2QRCGjcmy7_fF7T5" /></figure><p>Understanding how to identify the phases and the characteristics that make up each phase is only the first step in the process of team development. Developing strategies to move a team through higher levels of maturity is key, as is understanding what may cause a team to move back to a previous phase.</p><p>In Part II of this blog post, we will explore Tips and Tricks to developing a high performing Agile Team so keep your eyes open for it! It is my sincere hope that you found this post helpful, feel free to leave comments or suggestions and to share your thoughts.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=24495db82951" width="1" height="1" alt=""><hr><p><a href="https://medium.com/stuart-engineering/creating-and-developing-high-performance-agile-team-part-1-the-benefits-of-high-performance-24495db82951">Creating and Developing High Performance Agile Team — Part 1: The benefits of High Performance…</a> was originally published in <a href="https://medium.com/stuart-engineering">Stuart Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Agile reviews: How we adapted an engineering best practice]]></title>
            <link>https://medium.com/stuart-engineering/agile-reviews-how-we-adapted-an-engineering-best-practice-6f07104d4364?source=rss----e5dec0da746d---4</link>
            <guid isPermaLink="false">https://medium.com/p/6f07104d4364</guid>
            <category><![CDATA[agile-coaching]]></category>
            <category><![CDATA[scrum-master]]></category>
            <category><![CDATA[agile]]></category>
            <category><![CDATA[team-collaboration]]></category>
            <category><![CDATA[code-review]]></category>
            <dc:creator><![CDATA[Lena Lorenz]]></dc:creator>
            <pubDate>Thu, 02 Sep 2021 09:47:37 GMT</pubDate>
            <atom:updated>2021-09-02T09:47:34.760Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>Learn how Stuart’s Agile team developed a practice of Agile reviews, inspired by code reviews, to boost quality, knowledge, and collaboration.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/723/1*s7H-hsFeJwMtg9os5M9TKg.png" /></figure><p>As Agile Practitioners, we are normally the only Agile Practitioner on the team. We do not have someone questioning our decisions, besides the development team (always and nonstop 😉 — which we appreciate!). But we do not have another person from the same profession challenging what we are doing. Who makes sure that we deliver high-quality work? How do we make sure to learn from each other? How do we get new ideas? How do we stay on top of our game? How do we make sure to be aligned in our ways of agile development? And how do we make sure that everyone can handle the workload and pushback we get from the teams?</p><p>Recently, these questions were flying around in the Stuart Agile team and we were looking for ways to solve them. Of course, we implemented initiatives, which mitigated some of these risks. For example, we created “brain meeting”, a space to deep-dive into agile discussions and align on practices. Or team meetings to talk about organisational topics, workload, and happiness. But we asked ourselves: Is that enough?</p><p>What would we suggest to our development teams if they had the described issues or questions?</p><p>If this conversation was with our development teams, what would we suggest they do? And the answer was super clear: Code reviews.</p><p>Why is this standard tool, which we expect all of our development teams to use, not part of our way of working? We took the decision to look into it and figure out how we could adapt the best practice of code reviews to our work as agile practitioners.</p><h3>What we proposed</h3><p>First, we needed to clarify what our “code” was, or in other words, what exactly we wanted to be reviewed. We brainstormed some ideas around this and quickly found out this was not a trivial topic. We decided to call it “deliverables”, which included things like workshops, trainings, retrospectives, kick-off meetings, how we do stand-ups, etc. It was clear that not all these deliverables could be reviewed in the exact same way or with the same cadence if we wanted to make the process effective and avoid overwhelming the team.</p><p>For the deliverables which consisted of something we created (a training, documentation, an article…) it was apparent that we could just review it asynchronously and collect feedback. The challenge was for the non-tangible things, such as reviewing certain meetings. In those occasions, we suggested organising a short session, around 30 minutes, with the reviewer to have a conversation. The proposed agenda for those conversations was:</p><ul><li>Giving context about the team and the goal you had in mind with the session</li><li>Explaining how the session went (sticking to the facts) and what the outcome was</li><li>Commenting on challenges and potential issues you encountered during the session: for instance, things that didn’t work as expected, problems with engaging people, pushback, etc.</li><li>Finally, the reviewer gives feedback on all the points above and suggests alternatives when appropriate.</li></ul><p>Additionally, as this was a new thing, we proposed to have a last part of the session in which we collected feedback about the session itself. We wanted to know if the reviews were useful for the team and to spot potential areas of improvement.</p><p>The next question we needed to answer was when the reviews should happen. Should we do one every time we work on something intended to help our teams? After every stand-up? When does it make sense to complete an agile review and when doesn’t it?</p><p>We concluded that this also depended on what we were reviewing. So we proposed dividing deliverables into two groups: Deliverables that need reviews every time and deliverables that only need it periodically.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*jq8pJR0U3q2ZLQXq" /><figcaption><em>Brainstorming for deliverables that need to be reviewed every time they happen</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*t9ZLYh8gTNAW6xxb" /><figcaption><em>Brainstorming for deliverables that need to be reviewed periodically</em></figcaption></figure><p>Deliverables that required review every time included things such as the retrospective outcomes, training, big changes happening in the teams, and kick-off sessions for new teams.</p><p>On the other hand, deliverables that don’t require review every time included things like one-to-one sessions, refinement sessions, preparations for retrospectives, and stand-ups. In this group, we agreed the reviews could be requested on-demand when we felt it was necessary, but also established a baseline cadence for reviews. For instance, every quarter we will review how we are approaching stand-ups.</p><p>Finally, we proposed the creation of a specific Slack channel to request these reviews. Any member of the Agile team could volunteer to review any deliverable from the others. Multiple reviewers were also allowed where it made sense.</p><p>Ah! We also needed a name, since Code Reviews didn’t make sense for us. So we decided to simply call our new method Agile Reviews.</p><h3>Implementation of the idea</h3><p>After developing the idea we did two test reviews to try it out and to get an idea of how it feels to be reviewed and to be the reviewer. Also, we wanted to get some initial ideas on where we could improve. With the insights from these tests, we made some adjustments to the Agenda and to the expected time needed for specific points.</p><p>We also talked to some developers to deepen our knowledge about code reviews and figure out some tips and tricks from them. The main insight from these talks was that we should add the following ground rules:</p><ul><li>Making sure to give feedback with empathy</li><li>Not being too picky</li></ul><p>As the next step, we presented the idea to the team, discussed details and got their buy-in to try it out. One learning we had from that session was that we had to define the deliverables more in detail as well as making the goals and not-goals very explicit.</p><h4>Goals:</h4><ul><li>Learn from each other</li><li>Ensure the quality of our work</li><li>Align on “how to do things” (identify areas in which we want to be aligned, similar to coding standards in the engineering world)</li><li>Help maybe identify blockers or hidden problems</li><li>Identify hidden personal struggles (is the agile practitioner happy in the team, stressed, unhappy, any personal issues in the team)</li></ul><h4>Not goals:</h4><ul><li>Controlling</li><li>Comparing</li><li>Having everyone work the same way</li></ul><h3>How did it go?</h3><p>We have been using agile reviews for three months in the team and so far we have done over 60 reviews in a team of eight people (including the review of this blog article!).</p><p>The feedback has been generally positive, helping us improve the quality of the work we deliver to the teams. It has also helped us to share information. A good example of this was a small presentation about how to improve the way we give and receive feedback. This presentation was originally created for a specific team to help them address some issues. However, after doing the agile reviews, some of the reviewers had an interest in using the same materials for their respective teams.</p><p>Another thing we can observe from this period is that there is a lot of interest in knowing what others in the Agile team are doing. When preparing this proposal, one of our concerns was that it could become difficult to find volunteers to be reviewers and that it could create too much overhead in the team. Far from it! What we have seen is that on many occasions there are multiple volunteers for the reviews. This is great because we are enabling people to share and learn new things, and also allows the reviewee to get more opinions.</p><h4>Feedback</h4><p>We can corroborate that we achieved some of our goals with the feedback that we have collected. Some examples are:</p><ul><li>“Overall it has helped me see other structures and different points of view.”</li><li>“I like the feedback very much (when I present something that I did), it’s an improvement goal for me and it works.”</li><li>“The quality of our work has gone up, as has team collaboration and engagement.”</li><li>“I feel the sharing of ideas has been really fruitful, it allows us to synthesise our thoughts and to learn from one another”</li></ul><p>It’s also fair to mention that some people expressed they got a bit anxious about the reviews, the most recurring points being:</p><ul><li>Fear of missing out on what was going on if they couldn’t review certain things</li><li>Having a bit of a “competition” feeling when they were not submitting things for review while others did</li><li>Introducing the reviews may have slowed down our speed as sometimes the process takes too long</li></ul><p>These are undesired side effects and we are going to discuss them with the team. But we believe we can learn a lot from these insights as well. For example, developers may have similar concerns about code reviews and this experience can help us to understand their feelings.</p><h4>Conclusions</h4><p>Overall, we are happy with the results and we have learned a lot from them. This practice will definitely continue and, at the same time, we will keep collecting feedback and applying changes to remove pain points and help participants to feel less anxious.</p><p>After this experience, we are convinced that, in the same way, those code reviews are a best practice for development teams. Agile reviews are a great way to boost the quality, knowledge, and collaboration in a team of agile practitioners. We would definitely recommend applying something similar to all the agile teams out there!</p><p>Written by <a href="https://www.linkedin.com/in/angel-sebastian-esparza-0096111a/">Àngel Sebastián Esparza</a> and <a href="https://www.linkedin.com/in/magdalena-lorenz-45b30910a/">Magdalena Lorenz</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6f07104d4364" width="1" height="1" alt=""><hr><p><a href="https://medium.com/stuart-engineering/agile-reviews-how-we-adapted-an-engineering-best-practice-6f07104d4364">Agile reviews: How we adapted an engineering best practice</a> was originally published in <a href="https://medium.com/stuart-engineering">Stuart Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How I researched complex processes at Stuart from scratch.]]></title>
            <link>https://medium.com/stuart-engineering/how-i-researched-complex-processes-at-stuart-from-scratch-a03535ed56c9?source=rss----e5dec0da746d---4</link>
            <guid isPermaLink="false">https://medium.com/p/a03535ed56c9</guid>
            <category><![CDATA[product]]></category>
            <dc:creator><![CDATA[Anfisa Novikova]]></dc:creator>
            <pubDate>Mon, 09 Aug 2021 09:02:31 GMT</pubDate>
            <atom:updated>2021-08-09T09:02:29.342Z</atom:updated>
            <content:encoded><![CDATA[<p>Or a short guide about how to break down complexity and translate it to UX deliverables.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_1mntGQLNE9gIQMm8ROFKQ.png" /></figure><p>9 months ago I joined Stuart as a Product Designer, and started my journey into the world of logistics. I have never worked in such a dynamic and fast-changing environment before, and one of my biggest challenges was to quickly incorporate and research the Supply and Demand side of logistics from scratch.</p><p>Now, looking back to the beginnings of my journey at Stuart I can say it’s been an amazing time when I rapidly learned a lot; I believe I selected the appropriate set of exercises and I am excited to share it with the community. Maybe it will help another designer to start their journey smoothly into the complex domain!</p><h3><strong>Principal advice: Set your research goals.</strong></h3><p>When I joined Stuart we did not have a dedicated User Research Department, nor any urgent upcoming project. I adapted to the current situation (the cross-functional team I joined was in search of engineers, and the roadmap for the team was not defined yet) and set the goal of my research by myself, which was to <strong>holistically research the topic</strong> of Supply and Demand to <strong>get myself prepared for future projects.</strong></p><p><strong><em>Do:</em> </strong>Talk to your manager, PM/PO, or whoever made a decision of you joining the project to understand what was the reason you are in the project/company, and/or collect their vision/expectations. Try to find out the current state of the project, what is the biggest need/problem the company needs to address. Is there any research done already about that topic? Depending on the answer, you should think of your research goals and methods.</p><blockquote><em>Below I am sharing some advice based on my experience researching complex processes from scratch — meaning there was no research done before I joined, and no relationship established with my users and stakeholders.</em></blockquote><h3><strong>Advice #1: Know who your user is.</strong></h3><p><strong><em>Do:</em></strong> It’s extremely important to connect to the right people and ask them the right questions. How to identify the right people? Check the organisational chart, internal documentation (Confluence, Notion, etc.), simply ask whoever is onboarding you which people you should meet. Along with learning about company culture, building the right network are the most important steps of a newbie.</p><h4><strong>UX deliverable</strong></h4><p><strong>Users- or stakeholders map.</strong> It’s needed to know the scale of your research (so you can estimate timeframes), and map out existing relationships (consider both official and unofficial), so once you start talking to your users you can link workflows/insights and grab a bigger picture.</p><h3><strong>Advice #2 &amp; 3: Know what you’d like to learn from users, and what you’d like to achieve from the first touchpoint.</strong></h3><p>It sounds very cliche, but indeed it’s crucially important to identify what you would like to take from your touchpoint(s) with the user. Talking to experts in their domain could be challenging, because they have <strong>so much specific knowledge</strong> to share they simply might not know how to pass it on.</p><p><strong><em>Do:</em></strong> Ask the right questions and frame the interview. Consider studying internal documentation of the department your user is working in if possible, and/or sector-related materials so you can have a better understanding of users’ pain points. Ask them what kind of projects they participate in, who they closely work with, what kind of tools they use, etc. Also, if you plan to collaborate closely with your users in the future, remember that the first touchpoint is not only about you learning about your user, but also an opportunity to get a buy-in from them! Explain what Product/UX design is about and how you expect the relationship with that person to look in the near future — maybe you are already planning some shadowing sessions, usability testing, or a second round of interviews?</p><h4><strong>UX deliverable — 1</strong></h4><p><strong>Interview guide.</strong> First think of what you’d like to learn (goals), then translate it into questions (interview guide). Don’t forget about timeframing, introduction, ice-breaker (more in the next advice), closing and Q&amp;A part.</p><h4><strong>UX deliverable — 2</strong></h4><p><strong>Interview documentation.</strong> Once interviews/touchpoints are over, it’s important to document what users have shared with you. This can be a simple transcript where you group insights, empathy map, user journey, jobs to be done, etc.: whatever suits your research goals best, and would help you in the next phase of your research/project.</p><h3><strong>Advice #4: Treat your users as people, not robots.</strong></h3><p>Even though your users might do repetitive tasks and have very little “human” part in their daily routine, we are human after all!</p><p><strong><em>Do:</em> </strong>I always like kicking off the first interview by asking my users how they found their way to their workplace, what they like and dislike about their job, and what their daily motivation is. It always helps to break the ice and establish a human connection. Understanding what motivates your user is great knowledge you can benefit from in your future research and design processes!</p><h4><strong>UX deliverable</strong></h4><p>Ice-breaker part in your interview guide.</p><h3><strong>Advice #5: Sharing is caring.</strong></h3><p>Sharing your learnings, especially when you start investigating a topic no one has ever touched before, could be the most helpful thing you can do for your team and company. Even if you don’t know who can benefit from your research outside of your team, it’s still important to keep it accessible.</p><p><strong><em>Do:</em></strong> Document your research in such a way that it can be shared to the biggest range of people. Share it actively (on Slack, email, calls) and passively (make it searchable).</p><h3><strong>Advice #6: Find your way to dive deeper into the problematic topic.</strong></h3><p>Once you feel comfortable with the domain knowledge, do not hesitate to run a second round of investigation.</p><p><strong><em>Do:</em></strong> Find a method that suits your project goal best. Talk to the PM/PO if needed to align on what you’d like to clarify and what level of detail is needed. For the project we run I scheduled a set of shadowing sessions with a few users, where we asked them to perform their tasks and explain the decision-making process in detail.</p><h4><strong>UX deliverable</strong></h4><p><strong>Depends on the problem/project.</strong> It could be an interview, shadowing/screen-recording session, survey, etc. As I was dealing with complex processes, I always liked to validate process diagrams I created after every shadowing session with users to be sure I interpreted all that I saw correctly.</p><p><em>After running all those exercises I closed the initial part of my research and really felt confident to embrace upcoming challenges (I am on it right now!). During my research process I heavily relied on </em><a href="https://www.nngroup.com/"><em>Nielsen Norman group</em></a><em> materials, here happily sharing some of them:</em></p><p>To watch:</p><ul><li><a href="https://www.youtube.com/watch?v=NHevdlqoOaQ">Complex Apps 101</a></li><li><a href="https://www.youtube.com/watch?v=P1CYZ65b5SA">Design Patterns For Complex Apps and Workflows</a></li></ul><p>To read:</p><ul><li><a href="https://www.nngroup.com/articles/complex-application-design-framework/">Complex application design framework</a></li><li><a href="https://www.nngroup.com/articles/stakeholder-analysis/">Stakeholder Analysis for UX Projects</a></li><li><a href="https://www.nngroup.com/articles/task-analysis/">Task Analysis: Support Users in Achieving Their Goals</a></li></ul><p>What about you? Have you ever had an experience dealing with complex processes research? What would your advice be?</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a03535ed56c9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/stuart-engineering/how-i-researched-complex-processes-at-stuart-from-scratch-a03535ed56c9">How I researched complex processes at Stuart from scratch.</a> was originally published in <a href="https://medium.com/stuart-engineering">Stuart Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ A practical guide to removing frustration with Jest and Jenkins]]></title>
            <link>https://medium.com/stuart-engineering/a-practical-guide-to-removing-frustration-with-jest-and-jenkins-cc73dd332152?source=rss----e5dec0da746d---4</link>
            <guid isPermaLink="false">https://medium.com/p/cc73dd332152</guid>
            <category><![CDATA[jest]]></category>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[solutions]]></category>
            <category><![CDATA[typescript]]></category>
            <dc:creator><![CDATA[Sergi Castellsagué Millán]]></dc:creator>
            <pubDate>Thu, 03 Jun 2021 14:02:27 GMT</pubDate>
            <atom:updated>2021-06-03T14:02:25.981Z</atom:updated>
            <content:encoded><![CDATA[<h4>How the Solutions team at Stuart handles automated testing</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/670/1*AzN_MgnW2c7mh6UpxDg20w.png" /></figure><p>It was blood, sweat, and tears for Stuart’s Solutions Engineers team until we found the right combination of configurations in order to ensure our tests using <a href="https://jestjs.io/">Jest</a> <strong>always </strong>pass.</p><p>Before jumping to our recommendations, we’ll set the scene on who we are and what we’re trying to do.</p><h3>👋 Context: The Solutions Team</h3><p>In January 2020, we created a new team within Stuart Tech: the <em>Solutions Engineers team</em>.</p><p>The team’s purpose is to bridge the gap that exists between the business and the products that we have at Stuart. If the business team wants to trial a new business use-case, they ask for support from the new solutions engineers team. We then build solutions to unblock their needs in a short period of time.</p><p>To put it in another way: We are a startup inside a startup!</p><p>The team mainly works on projects with a short life span, adding the maximum value in the least amount of time possible. To achieve this, we had to make technical decisions on our development stack. That’s why our Solutions stack looks like this:</p><ul><li>Node JS and Express JS (using Typescript) for the backend;</li><li>React JS for the frontend;</li><li>NPM as the package manager;</li><li>Jest for tests;</li><li>ESlint as linter;</li><li>Jenkins as CI.</li></ul><p>All these tools are set up using a monorepo, in which we share all common code between Solutions in a very easy way.</p><h3>🤔 The problem</h3><p>Jest is a resource eater. Doesn’t matter if you have 8GB, 16GB, or 32GB of RAM. <strong>Jest will use it all. </strong>How about the CPU? Exactly the same. Two cores? Yummy! Eight cores? Yuuuummy! 😋</p><p>This is a pretty interesting situation. We are very used to running software that is extremely badly optimised. So badly that most of it usually uses a single CPU and just a few MB or GB of RAM, because the single-threaded application acts as a bottleneck. But that’s not the case of Jest. Jest adapts to your maximum resources and drains them all.</p><p>This is why all over the internet, you can very easily find recommendations for forcing Jest to run in a single thread, or a maximum of 50% of your CPUs.</p><p>And luckily, you can do it quite simply by setting parameters in Jest such as <strong>maxWorkers. </strong>This will be enough for development purposes. But… How about when running it in a CI tool such as Jenkins? If you have 4 workers available per Jenkins node, then 4 Jenkins jobs can be executed in parallel.</p><p>In the scenario of running 4 jobs in parallel with Jest’s <strong>maxWorkers</strong> to <strong>1</strong>, you’ll end up having 4 CPUs consuming 100% <em>(not exactly, but for the purpose of this example, close enough!).</em></p><p>On the other hand, you cannot control Jest RAM usage. It’ll use all your RAM if needed. So again, if you have 16GB of RAM, your individual Jest executions, running in the same Jenkins node, will try to consume 16GB each!</p><p>Because of the high CPU and RAM usage, the Jenkins daemons will not be able to communicate with the controller node—and the whole Jenkins execution will fail.</p><p><strong>😱 We need to prevent Jest from killing our nodes!</strong></p><h3>👩‍🏫 Our approach</h3><p>Our Jenkins machines are 15GB machines, with 4 workers per Jenkins node. As a result, we can have up to 4 Jest commands running at the same time in the same machine.</p><h4>Limiting the CPU</h4><p>Thanks to Jest, this is extremely easy. Just use:</p><blockquote>--maxWorkers=20%</blockquote><p>Why 20%, you might wonder? Simple maths: 4 (Jenkins workers) * 20% of the CPU = 80%. We’ll leave 20% for the system (so that the Jenkins Daemon can still respond, OS stuff, etc.).</p><h4>Limiting the RAM</h4><p>Here’s the biggest challenge. Jest doesn’t allow you to set a maximum of RAM. What’s even worse, Jest won’t trigger any Garbage collecting process unless it is running out of RAM <strong>or</strong> <a href="https://dev.to/pustovalov_p/reducing-jest-memory-usage-1ina">you force it to do so</a>. That means that the RAM usage will increase and increase and increase—up until the point where GC is triggered. If that’s not enough to free some memory (:cough: leaks :cough:), swapping starts to happen. By then, tests are running <strong>really, really slow.</strong></p><p>In order to limit the RAM, we went for the Docker approach. For every test suite we want to run, we spin up a Docker image and run it while limiting its resources based on the host resources.</p><p>Considering that our Jenkins nodes have 4 workers, we need to limit the resources based on the maximum available RAM, which in our case is 15GB.</p><p>We need to understand that RAM behaves slightly differently than the CPU. It takes time for RAM to be fully used, so in this case we can be a little more aggressive in allocating the resources. In our case, we decided to set the Docker limit to 4.3GB. 4.3GB * 4 = 17,2GB — slightly above the RAM limit.</p><p>How can you limit the RAM in Docker, you might wonder?</p><blockquote>docker run <strong>-m 4300m </strong>tests_image</blockquote><h4>Both things together</h4><p>If you really want to make sure Jest doesn’t drain all your CPUs, you can also benefit from Docker’s CPU limitations. Our host has 8 logical CPUs. So to be consistent with the 20% CPU limitation we set on the Jest command, we will set a CPU limitation on every Docker container to consume up to 6.5~ logical CPUs.</p><p>Again, as we will be running a maximum of 4 dockers in parallel in a Jenkins node, that’s around 1.5 CPUs.</p><blockquote>docker run <strong>--cpus=’1.5’</strong> tests_image</blockquote><p>And that, in combination with the RAM setting, becomes:</p><blockquote><strong>docker run -m 4300m --cpus=’1.5’</strong></blockquote><h3>🏇 Does this speed up tests?</h3><p>It depends…</p><p>Our biggest pain point was our Jenkins agents dying because of high CPU and RAM usage. With this approach, we resolved that issue.</p><p>It does improve speed for the test suites that are small. For those that eventually consume close to 4GB of RAM, the tests run slowly. This is not a big deal, because thanks to our Jenkins setup built by our DevOps team, the Jenkins nodes are auto-scaled. By using Jenkins pipelines, we can simply parallelise as many tests as we want into different Jenkins jobs.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cb36c41dac1fc244cd81fbcb119c9989/href">https://medium.com/media/cb36c41dac1fc244cd81fbcb119c9989/href</a></iframe><h3>🚰 Working around the memory leaks</h3><p>In order to improve tests’ performance and ensure they do not slow down because they are consuming too much RAM, our recommendation is not to run all tests in a single Jest command.</p><p>Instead, divide your tests into multiple executions. In our case, we have a monorepo with more than 10 applications. We have divided these 10 suites into 5 different Jest executions. By restarting Jest, you’ll make sure you start from 0, and not with previous leaks affecting your current tests.</p><h3>🐌 Still too slow?</h3><p>It could be that soon after starting running the tests, they’ll already be running slowly. It’s then highly likely that you’ve already hit the max RAM allowed. To confirm this, run “<strong>docker stats”. </strong>It will show the current memory usage and its limit.</p><p>If the usage hits 100% very quickly—unless you find a big memory leak and manage to resolve it—the only solution is to increase docker RAM: run the test suite without setting any docker limitations, and while monitoring with <strong>docker stats, </strong>write down the highest peak.</p><p>If the peak is way beyond the RAM you have set to the Docker container, you should increase the RAM limits in the docker container, <strong>but also </strong>make sure that the maths still make sense. If we had to increase our RAM further for our tests, we’d have two options:</p><ol><li>Increase nodes RAM from 15GB to 32GB.</li><li>Reduce the Jenkins workers from 4 to 3.</li></ol><h3>🏁 Conclusions</h3><p>We spent a couple of weeks with a trial and error approach until we found the perfect numbers for our infrastructure. You should not expect to copy-paste any configuration.<strong> You need to understand your infrastructure and adapt to it.</strong></p><p>We also found it pretty interesting to learn that a tool which makes use of all the hardware available is, in reality, a problem. We are so used to running programs in over-dimensioned hosts that when it’s not the case, we struggle to figure out how to <em>fix that</em>.</p><p><em>Would you like to see more? We are hiring! 🚀 </em><a href="https://stuart.com/jobs/"><em>Check out our open positions</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cc73dd332152" width="1" height="1" alt=""><hr><p><a href="https://medium.com/stuart-engineering/a-practical-guide-to-removing-frustration-with-jest-and-jenkins-cc73dd332152">🔍 A practical guide to removing frustration with Jest and Jenkins</a> was originally published in <a href="https://medium.com/stuart-engineering">Stuart Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Boosting Parallel INSERTs]]></title>
            <link>https://medium.com/stuart-engineering/boosting-parallel-inserts-745a4df8033b?source=rss----e5dec0da746d---4</link>
            <guid isPermaLink="false">https://medium.com/p/745a4df8033b</guid>
            <category><![CDATA[insert]]></category>
            <category><![CDATA[ruby]]></category>
            <category><![CDATA[multithreading]]></category>
            <category><![CDATA[sql]]></category>
            <category><![CDATA[performance]]></category>
            <dc:creator><![CDATA[Xavier Noria]]></dc:creator>
            <pubDate>Mon, 15 Mar 2021 16:48:02 GMT</pubDate>
            <atom:updated>2021-04-01T18:33:38.926Z</atom:updated>
            <content:encoded><![CDATA[<h3>🏁 Boosting Parallel INSERTs</h3><h4>How we dramatically increased our database write speed</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/0*nXdclZdU9L-tlOiN.jpg" /></figure><h3>Optimization Target</h3><p><a href="https://stuart.com/">Stuart</a> is a last-mile delivery company. When riders are active in the platform their phones send <strong>GPS locations</strong> so the application knows where everyone is.</p><p>Servers are hammered with GPS locations, so this is a part of the system were optimizations may have a significant performance impact. On reception, each GPS location gets inserted into MySQL, and then a number of things follow. (This is a simplification, but faithful enough for the purposes of this post.)</p><p>The business is growing fast, and MySQL started to struggle with the load during peak hours. This continued up to the point where manual intervention from the on-duty team was needed to reduce the pressure on the platform.</p><p>While some long-term solutions were being worked on, we needed a guerrilla tactic to win time and regain tranquility until those solutions landed.</p><p><strong>We wanted to optimize MySQL INSERTs for the same entity performed by separate threads.</strong></p><p>These threads could be the ones responsible for HTTP endpoints in a web server. Or they could be BEAM processes in a <a href="https://phoenixframework.org/">Phoenix</a> application. In our case, threads execute as <a href="https://sidekiq.org/">Sidekiq</a> workers, but conceptually they all represent the same scenario.</p><h3>Bulk Data Loading</h3><p>Before we explain the technique, let’s take one step back.</p><p>Let’s imagine you need to import 1,000,000 records into a database in a batch process. Which are the options?</p><h4>Sequential INSERTs 🐌</h4><p>The simplest approach is to write a loop that inserts them one after the other.</p><p>In Ruby:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1692b0643cc96e6a63f520049a4f6081/href">https://medium.com/media/1692b0643cc96e6a63f520049a4f6081/href</a></iframe><p>It works, and it may be enough for a small number of records, but it is really inefficient for a large set.</p><h4>Parallel INSERTs 🚀️️️</h4><p>The next step is to spawn <em>N</em> threads, with their own database connection each, and split the work among them.</p><p>In Ruby:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/43b7d17847b5e945f1791fb92019aca4/href">https://medium.com/media/43b7d17847b5e945f1791fb92019aca4/href</a></iframe><p>As a rule of thumb, this may speed things up to nearly <em>N</em>x.</p><p>It’s a helpful technique for batch processes. In the problem at hand, however, applications already leverage this as a side-effect of being multi-threaded.</p><h4>Bulk INSERTs 🚀️🚀️</h4><p>But we can still do better. One single INSERT statement is able to insert multiple rows at once. In this code snippet we insert three rows:</p><pre>INSERT INTO <em>table_name<br>  </em>(col1, col2, col3)<br>VALUES<br>  (val11, val12, val13),<br>  (val21, val22, val23),<br>  (val31, val32, val33);</pre><p>This is <em>way faster</em> than doing three individual INSERTs.</p><p>MySQL documentation has a <a href="https://dev.mysql.com/doc/refman/8.0/en/insert-optimization.html">page</a> dedicated to this optimization where you can see the server works less.</p><p>Another important difference is the amount of network round-trips: Every statement you execute has a response of some sort from the database. In a procedural API, you issue the INSERT, wait for the server to commit the transaction and reply, process the reply, next record, etc.</p><p>Therefore, if you issue 1,000 sequential INSERTs, the client needs to wait for MySQL and process its response <em>1,000 times</em>. That waiting is interleaved with all the operations. On the other hand, if you execute one single INSERT with 1,000 rows, the client waits for MySQL <em>once</em>.</p><p>All these observations compound, and therefore <em>the bigger the batch size, the bigger the speed up</em>. To show you the magnitude of the acceleration, these are some factors that I have seen:</p><pre>Groups of    5 →   5 times faster<br>Groups of  100 →  57 times faster<br>Groups of  500 → 189 times faster<br>Groups of 1000 → 284 times faster</pre><p>Factors depend on latency, complexity of the statement, etc. But in any case, for a back-of-the-napkin calculation, we can see an opportunity lies in here for huge performance improvements.</p><h4>CSV Imports 🚀️🚀️🚀️</h4><p>Can we still squeeze more rows per time unit? Yes!</p><p>Importing data in CSV format is even more performant. However, we’ll leave it at bulk INSERTs for this post.</p><h3>Connecting the Dots</h3><p>The idea of the technique is to apply this to a dynamic situation where you are receiving a data stream in a multi-threaded endpoint.</p><p>There are several ways to do this, and the trade-offs depend on the details of each particular situation. We implemented a process-level buffer and a <a href="https://www.rubydoc.info/gems/concurrent-ruby/Concurrent/TimerTask">timer thread</a>. GPS locations received are pushed to the buffer. Every second, that buffer is flushed. Done.</p><p>Simple, but the impact is dramatic.</p><h4>Ruby Implementation</h4><p>The problem statement in this post, and the technique we use to address it are quite generic. However, let’s see some concrete source code in Ruby.</p><p>The Sidekiq worker that receives locations only pushes to a buffer. Can’t be more cheap:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/beb2cee543c890306d1b09eb8faf6232/href">https://medium.com/media/beb2cee543c890306d1b09eb8faf6232/href</a></iframe><p>Schematically, this is the batch writer:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bff83ad5bdbd72ead4e28a261a932d1e/href">https://medium.com/media/bff83ad5bdbd72ead4e28a261a932d1e/href</a></iframe><p>I like the clarity of the the timer thread, which allows you to unit test flush properly in a deterministic manner. The timer thread is not needed in the test suite beyond a trivial coverage that guarantees it flushes as expected.</p><p>You could also flush on buffer size. But a time threshold is normally necessary because otherwise flushing could be too infrequent in valleys. In an Elixir application where I have written something similar for <a href="https://aws.amazon.com/dynamodb/">DynamoDB</a> I have both logics in place: buffer size, and at most <em>t</em> time between flushes.</p><p>You have to choose a value for MAX_BATCH_SIZE that makes sense for the application. Let’s suppose your workload today is less than 100 records per second per process. If you set MAX_BATCH_SIZE to 100, when the platform is quiet maybe you send batches of 15, and when the platform is busy maybe you send batches of 75.</p><p>Here’s the point: As we have seen above, batches of 75 are way more efficient than batches of 15. Thus, <strong>the busier the system, the faster it runs</strong>.</p><p>Batch insertion can be done in different ways. My input is a trusted hash table and I build a raw SQL string that is executed directly with <a href="https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-exec_insert">exec_insert</a>. If you want to work with ARs, Active Record 6.0 shipped with <a href="https://api.rubyonrails.org/classes/ActiveRecord/Persistence/ClassMethods.html#method-i-insert_all">insert_all</a> and friends. The gem <a href="https://github.com/zdennis/activerecord-import">activerecord-import</a> is also worth mentioning.</p><p>Finally, we need to ensure the buffer is flushed when the server shuts down:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6350d88cd4cba1e7f97f7f5d4d4f3ace/href">https://medium.com/media/6350d88cd4cba1e7f97f7f5d4d4f3ace/href</a></iframe><h4>Caveats</h4><p>Due to buffering, there’s a small delay between reception and availability of the event in the platform.</p><p>On the other hand, MySQL does not give you the IDs of the new records. If the primary key is auto-incremented, however, MySQL guarantees they are consecutive. Thus, if you need them, you can retrieve the <a href="https://dev.mysql.com/doc/refman/8.0/en/information-functions.html#function_last-insert-id">last ID inserted</a> and do the math.</p><h4>Summary</h4><p>We’ve seen a way to boost parallel INSERTs with a per-process buffer flushed regularly using one single INSERT with multiple rows.</p><p>It’s been a smooth sailing since this was deployed. 🎉</p><p><em>Like what you see? </em><a href="https://apply.workable.com/stuart/j/E21D36E274"><em>We’re hiring!</em></a><em> 🚀</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=745a4df8033b" width="1" height="1" alt=""><hr><p><a href="https://medium.com/stuart-engineering/boosting-parallel-inserts-745a4df8033b">Boosting Parallel INSERTs</a> was originally published in <a href="https://medium.com/stuart-engineering">Stuart Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>