<?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[McKinsey Digital Insights - Medium]]></title>
        <description><![CDATA[Going beyond our insights and knowledge to connect you with McKinsey Digital. - Medium]]></description>
        <link>https://medium.com/digital-mckinsey?source=rss----9c56b966f13e---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>McKinsey Digital Insights - Medium</title>
            <link>https://medium.com/digital-mckinsey?source=rss----9c56b966f13e---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 17:52:41 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/digital-mckinsey" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Platform as code: Reference architectures to simplify developer platforms]]></title>
            <link>https://medium.com/digital-mckinsey/platform-as-code-reference-architectures-to-simplify-developer-platforms-f2fb48e0c874?source=rss----9c56b966f13e---4</link>
            <guid isPermaLink="false">https://medium.com/p/f2fb48e0c874</guid>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[mckinsey]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[developer-platform]]></category>
            <dc:creator><![CDATA[McKinsey Digital]]></dc:creator>
            <pubDate>Wed, 20 Mar 2024 13:52:02 GMT</pubDate>
            <atom:updated>2024-03-20T22:34:45.626Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Modern developer platforms can redefine software development and enable DevOps at scale. Reference architectures provide a starting blueprint for their design.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FPyBc74AjDgSUeu2pgQ3kQ.jpeg" /></figure><p><strong>By Stephan Schneider — Expert Associate Partner</strong>, <strong>Navin Agarwal — Principal Cloud Engineer, Thomas Delaet — Partner, and Remy Paternoster — Partner, </strong><a href="https://www.mckinsey.com/capabilities/mckinsey-digital/how-we-help-clients"><strong>McKinsey Digital</strong></a></p><p>How can tech leaders unleash the full potential of their software development talent? By empowering their developers, creating an environment ripe for innovation, and <a href="https://www.mckinsey.com/industries/technology-media-and-telecommunications/our-insights/yes-you-can-measure-software-developer-productivity">improving their productivity</a> with the right tools and processes. These factors contribute to what we call <a href="https://www.mckinsey.com/industries/technology-media-and-telecommunications/our-insights/developer-velocity-how-software-excellence-fuels-business-performance">“developer velocity”</a> — a key driver of business success. <a href="https://www.mckinsey.com/industries/technology-media-and-telecommunications/our-insights/developer-velocity-how-software-excellence-fuels-business-performance">Previous McKinsey research</a> has found that companies with top-quartile developer velocity outperform bottom-quartile companies by four to five times (measured in revenue CAGR).</p><p>Reducing the cognitive and operational load on developers is a critical step toward increasing product innovation and velocity. The idea of “you build it, you run it” has empowered software development teams to take ownership over their products and release them more frequently and with higher quality. However, it has also placed tremendous strain on software developers. They need to stay current on the latest software-engineering practices and tooling, maintain in-depth knowledge of running and integrating a large breadth of cloud service offerings, and deliver on the ever-increasing velocity of product output that many organizations expect.</p><p>A modern developer platform can go a long way toward boosting developer velocity and <a href="https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/tech-forward/why-your-it-organization-should-prioritize-developer-experience">elevating the developer experience</a>. By acting as a self-serve “one-stop shop” for developers, such a platform reduces manual work, increases process efficiency, and propels standardization, security, and compliance by design. In other words, it allows developers to focus on what they do best — writing code to solve a customer problem.</p><p>While every platform is different, certain best practices have emerged across companies and industries. Drawing from our experience, we have developed a set of design principles and a reference architecture that can serve as a useful blueprint for a best-in-class developer platform and enable DevOps at scale.</p><h3>Eight design principles for developer platforms</h3><p>Platform-engineering teams should follow a set of core design principles when building the developer platform:</p><ol><li><strong>Focus on the user. </strong><a href="https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/tech-forward/why-your-it-organization-should-prioritize-developer-experience">Because developers are the most important “customers” of a developer platform</a>, they need to be heavily involved in a platform’s design, prioritization of features, and testing to make sure it is fit for purpose, user-friendly, and fully self-service. A user-centric design that addresses the needs of developers can make all the difference and drive adoption.</li><li><strong>Run the platform team like a start-up. </strong>IT leaders should establish a small, central team that not only owns the platform but is also responsible for marketing it (and has the resources to do so). The team also needs sufficient capacity to ensure the successful implementation of the platform through community forums, technical support, and clear and concise documentation.</li><li><strong>Decentralize the contribution model.</strong> While ownership falls to a central team, the contribution model should allow for decentralized, continuous development. As in an open-source model, contributors — for example, application development (app dev) teams — should be able to share plug-ins and propose changes, but the platform team should retain the final say over what is approved and accepted in the platform. It is important to acknowledge that app dev teams across the organization are at different levels of maturity, capability, and engagement — meaning not all are ready or willing to contribute to the platform from day one. To establish an inner-sourcing culture, organizations will need to support an operating model in which teams with the right skills and knowledge can contribute. They also need to maintain an upskilling mechanism for teams to grow over time into contributing roles.</li><li><strong>Build golden paths, with some cages. </strong>Developers should be free to choose the abstraction level that suits their needs. While a developer platform should provide a set of <a href="https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/cloud-foundations-ten-commandments-for-faster-and-more-profitable-cloud-migrations">predefined patterns</a> or “golden paths” for developers to follow — in addition to some “cages” they are not allowed to leave — it also needs to provide flexibility for developers to implement more-complex use cases or edge cases.</li><li><strong>Drive standardization by design. </strong>Enabling self-service requires platform engineers to <a href="https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/cloud-foundations-ten-commandments-for-faster-and-more-profitable-cloud-migrations">establish secure foundations</a> and define preconfigured application patterns to ensure every resource created through the platform is secure, compliant, and well architected. However, application patterns only standardize the first deployment, after which teams can edit and drift away from the standard. To ensure resources stay compliant and in line with the architected application patterns, the platform needs a mechanism that enforces these standards with every single deployment — for instance, via policies on the cloud-provider level, via automated checks in the pipeline or source code, or via centralized components such as a platform orchestrator that enforces standardization by continuously generating apps and infrastructure against the pattern during deployment.</li><li><strong>Get the most from existing capabilities. </strong>Every organization has at least some individual components of a developer platform in place (such as <a href="https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/cloud-foundations-ten-commandments-for-faster-and-more-profitable-cloud-migrations">base and isolation zones in the cloud</a>), and standard components such as backlog management, continuous integration and continuous deployment (CI/CD) toolchain, and test suites are widely available in the market. A modern developer platform serves as the glue between these individual components and offers an opinionated set of design choices on how to use and configure them (baked as code into the platform) to fulfill developer needs.</li><li><strong>Let developers choose their platform interface. </strong>The developer platform should not break developer workflows or force developers to use any specific interface. A code-based workflow should be the default, with options to use a user interface (UI), command line interface (CLI), or application programming interface (API).</li><li><strong>Define success as usage.</strong> We find that the true marker of success is an organization’s ability to drive traffic to the platform and <a href="https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/tech-forward/why-your-it-organization-should-prioritize-developer-experience">entice developers to use it in their day-to-day work</a>. A platform that enhances the experience of developers and addresses their pain points can create pull from the organization. Ultimately, developers should use the platform because they want to, not because they have to. This can be achieved via gamification and rewards (for example, by rewarding app dev teams and individuals for faster adoption) or enforcing certain standards (which are out-of-the-box ready in the platform).</li></ol><h3>Developer platform reference architecture</h3><p>Modern developer platforms require interchangeable components and provide flexibility to suit both simple and complex (multicloud or hybrid) architectures. The exhibit below illustrates a commonly deployed AWS architecture that can provide a starting point for a modern developer platform. However, IT leaders should begin by assessing their existing architectures and developer pain points, and then incorporate the components that they already have in place. The components and tools referenced below exemplify an AWS-based setup. All components are interchangeable, and similar reference architectures can be implemented for GCP, Azure, OpenShift, or any hybrid setup. Individual components can be replaced with any homegrown, on-premises, or other cloud-native solutions.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9iNAR9pZ4NY2jsoj_5BHfA.png" /><figcaption><em>Commonly deployed AWS reference architecture</em></figcaption></figure><p>The developer platform reference architecture includes five main components:</p><ul><li>The <strong>developer control</strong> <strong>plane</strong> provides the interfaces through which developers can interact with the platform (IDE, API, CLI, GUI, GitOps, and so on). It is where developers can access the structured catalog with the components and documentation they need, and where they can implement, review, and deliver changes.</li><li>The<strong> integration and delivery plane</strong> has the tools that build, store, configure, and deploy all requests coming from the developer control plane — including CI/CD, registries, and platform orchestration.</li><li>The <strong>monitoring and logging plane</strong> provides real-time metrics and logs for applications and infrastructure, allowing developers to observe, monitor, and make data-driven decisions.</li><li>The<strong> security plane </strong>manages confidential information and identities to protect sensitive data — for example, by storing, managing, and securely retrieving API keys and passwords.</li><li>The <strong>resource plane</strong> includes all the components necessary to run applications delivered through the platform (as predefined in application patterns and workload specifications).</li></ul><p>To assemble these planes into an end-to-end architecture, the platform engineering team must wire the individual components of the planes to one another, wire one plane to the other, and test the raw end-to-end flows to ensure smooth interplay.</p><p>To shortcut this process, we have defined this reference architecture for multicloud setups as code (AWS, Azure, GCP, and OpenShift), which enables an organization to set up a basic developer platform within a few hours. The platform setups are codified as Terraform scripts, and the workload specifications for common application patterns (such as three-tier application, microservice, and message bus) are implemented both as Terraform scripts and by using the open-source workload specification Score. Later, a platform orchestrator (a rule engine that matches resource requests to predefined default configurations provided by the platform team) dynamically creates the final configurations on each deployment. Multiple options can come into play as orchestrator, including cloud-native (such as AWS CloudFormation), homegrown (such as using Terraform and Argo CD), or off-the-shelf (such as Humanitec) services.</p><h3>Contribution and governance model</h3><p>Because developer platforms are designed to enable standardization, security, and compliance, it is critical to clearly define the setup and governance of the repositories from the beginning. We differentiate between repositories owned by development teams and those owned by platform teams: the former is based on workload, and the latter is shared across the organization.</p><p>On the development team level, the workload-based repository should include the source code, workload specification, docker file, and pipeline YAML. The “traditional” app configurations and infrastructure-as-code (IaC) files are replaced with a workload specification. This allows developers to describe their architecture (the relationship to other workloads and dependent resources) in a general, abstract, and environment-agnostic way. For instance, a workload specification could read, “I am a workload of the name ‘Python service.’ I have a dependency on a database of type PostgreSQL, an AWS S3 bucket, and a DNS.”</p><p>Meanwhile, the platform team should be responsible for developing the following cross-cutting elements:</p><ul><li><strong>Resource drivers and infrastructure as code</strong>: A catalog of all resources made available by the platform team (such as cloud-native components and on-premises) and templates for dynamically created resources</li><li><strong>Resource definitions:</strong> Guidance on which resource drivers to use in a specific context; for example, “use this type of AWS S3 bucket” if the context of the deployment is a “staging” environment; use another one if it is “production”</li><li><strong>Workload profiles</strong>: Default configuration details for application configurations, such as CPU minimum allocation, labels and annotations, and sidecar containers</li><li><strong>Automation or compliance</strong>: Governance defined as code in the platform — for example, definitions of environment progression, deployment automations, and sign-off rules</li></ul><p>The objective of this governance model is to provide development teams with ownership over their code and freedom to deliver it to production — as long as it stays within the guardrails defined by the platform team.</p><p>The goal of this approach is to cover 80 percent of the organization’s requirements with a few app patterns and resources (centrally defined by the platform team) and enable the rest to be defined via a decentralized contribution model (such as an open-source model), with the platform engineering team retaining final approval over contributions.</p><p>Say a developer needs an ArangoDB database (and has a valid business reason to stray off the default components defined in the platform), but the platform doesn’t support the definition yet. Assuming the platform team grants the rights, the developer should then expand the resource definitions — effectively “teaching” the platform how and when to deploy an ArangoDB — and send a pull request to the organization-wide repository that the platform team can then review and approve. If the next user gets into a similar situation and the platform team has globally accepted the definition, the procedure is already fully automated.</p><p>Why is this approach “better” than the current CI/CD flow? Because it allows continuous standardization of the existing estate through the platform. Imagine updating labels and annotations across all your workloads or updating your PostgreSQL definition from version 14 to version 15. Or say you want to reuse infrastructure definitions without having to go through security review every time. This platform design makes doing so possible while enabling platform teams to drive standardization, security, and compliance by design. With this design, platform teams are also able to optimize the load and infrastructure configuration organization-wide (instead of by team) and drive the automation of <a href="https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/the-finops-way-how-to-avoid-the-pitfalls-to-realizing-clouds-value">FinOps policies</a> (FinOps as code) to reduce cloud spend systematically.</p><p>***</p><p>Developer platforms are a growing trend, but few organizations have fully cracked the code. By starting with a set of design principles and a reference architecture based on best practices and enterprise learnings, IT leaders can more confidently take the first steps toward building a modern developer platform and unlocking the full potential of their software development teams.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f2fb48e0c874" width="1" height="1" alt=""><hr><p><a href="https://medium.com/digital-mckinsey/platform-as-code-reference-architectures-to-simplify-developer-platforms-f2fb48e0c874">Platform as code: Reference architectures to simplify developer platforms</a> was originally published in <a href="https://medium.com/digital-mckinsey">McKinsey Digital Insights</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building a serverless application on Google Cloud to accelerate deployment time with reduced cost…]]></title>
            <link>https://medium.com/digital-mckinsey/building-a-serverless-application-on-google-cloud-to-accelerate-deployment-time-with-reduced-cost-467d1a82369d?source=rss----9c56b966f13e---4</link>
            <guid isPermaLink="false">https://medium.com/p/467d1a82369d</guid>
            <category><![CDATA[serverless-cloud]]></category>
            <category><![CDATA[service-provider]]></category>
            <category><![CDATA[google-cloud]]></category>
            <dc:creator><![CDATA[McKinsey Digital]]></dc:creator>
            <pubDate>Thu, 15 Feb 2024 17:19:50 GMT</pubDate>
            <atom:updated>2024-02-15T17:09:23.549Z</atom:updated>
            <content:encoded><![CDATA[<h3>Building a serverless application on Google Cloud to accelerate deployment time with reduced cost and complexity</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sfgZRjUX7E0_enEl__hXIw.png" /></figure><p><strong>By Maksood Mohiuddin — Senior Cloud Engineer II, Alex Dequevedo — Senior DevOps Engineer I, Thomas Neff — Senior DevOps Engineer I, and Marco Marulli — Principal Lead II</strong></p><p>Creating a modern, three-tier serverless application on a cloud service provider (CSP) given a containerized application is a common task for many cloud engineers. The challenge lies in being able to efficiently leverage the CSP’s breadth of services for different application tiers while expediting development efforts and reducing costs and complexity. In this article, we provide a step-by-step guide for deploying a modern, three-tier serverless application on Google Cloud that accelerates deployment time while reducing cost and complexity, along with detailed explanations of the services used in this implementation.</p><h3>Architecture overview</h3><p>We will use an example application titled “Amazing Employees,” in which we employ a three-tier microservices architecture that comprises the frontend (the presentation tier), backend (application tier), and a Firestore database (data tier). The frontend, which is developed using Angular, interacts with users, while the backend, which is built with Python Flask, manages business logic. “Amazing Employees” consists of a single frontend and backend container. However, microservices architectures are scalable: adding more containers can facilitate more features. Infrastructure for the application is defined through Terraform, and Cloud Build manages the application’s building and deployment.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ADrgYTMsFTyyK0uX59ce-A.jpeg" /><figcaption><em>Figure 1: Google Cloud three-tier serverless architecture.</em></figcaption></figure><h3>Why serverless?</h3><p>Serverless technology offloads much of the complex work of managing infrastructure to CSPs, enabling developers to concentrate on creating and deploying application code. Serverless offerings provide many benefits, including the following:</p><ul><li>nearly infinite automatic scaling</li><li>high availability</li><li>simplified infrastructure management</li><li>potential cost savings (pay only for what you use)</li><li>faster development cycles</li></ul><p>Serverless technologies help enable several highly scalable architecture types, such as a microservices architecture. Microservices break down applications into smaller, independently deployable units. With the ability to scale individual microservices dynamically and isolate faults, developers can create highly responsive and cost-effective solutions. While serverless technology is not required to create a microservices application, it greatly simplifies the management and scaling aspects, making it an ideal choice for organizations looking to optimize resource utilization and streamline development efforts.</p><p>Another common architecture for serverless applications is an event-driven architecture, in which applications respond dynamically to events triggered by various sources, such as user actions, data changes, or external system interactions. It allows developers to design systems that automatically scale in response to incoming events, ensuring optimal performance during peak loads and cost savings during quieter periods.</p><h3>Application services</h3><p>For the frontend and backend of our “Amazing Employees”<em> </em>application, we leverage Cloud Run, a fully managed, serverless container environment from Google Cloud. Cloud Run is based on Knative, a serverless container technology that Google Cloud pioneered. Cloud Run is often easier to use than other container orchestration services, such as Google Kubernetes Engine (GKE), because it abstracts the infrastructure layer away and there is no need to manage the underlying Kubernetes clusters. Cloud Run automatically scales containers based on incoming traffic. The latest version of Cloud Functions — another popular choice to build serverless frontends or backends in Google Cloud — uses Cloud Run behind the scenes.</p><p>Of course, what you gain in simplicity, you lose in control. This is true for all managed services in the cloud. If your use case requires you have full control over your containers (such as configuring networking, scaling, and resource allocation at a granular level), you may need to use GKE instead of Cloud Run. Whenever possible, we recommend using fully managed services such as Cloud Run because, simply put, they make your life much easier.</p><p>API Gateway is used to secure traffic between frontend and backend Cloud Run instances. Generally speaking, using API Gateway is a best practice when building a microservice architecture because it improves security, performance, and flexibility while reducing complexity. Google Cloud’s API Gateway has three main components: the API, the gateway, and the config. The config uses OpenAPI specs and can be viewed in the infra directory. Note that two variables are used, which allows us to deploy the config without making manual changes. The first variable is <em>${project_id}</em>, which is provided in <em>variables.tf</em>, and the second is <em>${url}</em>. The <em>${url}</em> variable value is provided by a Terraform data block that checks the URL of the backend Cloud Run instance and adds it to the config. In both cases, variable substitution is done through Terraform and can be viewed in <em>infra/api-gateway.tf</em>.</p><p>For the application database, Firestore was a natural choice due to its seamless integration into our serverless architecture. Firestore, a NoSQL database from Google Cloud’s Firebase service, was selected primarily for its ability to offer automatic scaling, robust querying support, advanced security rules, and dependable application authentication. In the context of our application, we use Firebase Authentication for user logins and employ basic CRUD database operations for managing employee data.</p><h3>Infrastructure and deployment</h3><p>For infrastructure as code, we use Terraform, an open-source tool that HashiCorp developed. Terraform allows you to define, manage, and provision infrastructure resources across various cloud providers, including Google Cloud, in a declarative manner.</p><p>Optionally, you can use Cloud Storage to store Terraform state. Cloud Storage is an object storage service within Google Cloud that can store unstructured data such as files and media and offers scalability to manage vast amounts of data across multiple storage classes. Cloud Storage offers data durability through replication and provides accessibility with low latency; security features such as access control and encryption; and versioning, life cycle management, and integration with other Google Cloud services.</p><p>To build the frontend and backend for the application, we use Cloud Build, a serverless continuous integration (CI) and continuous delivery (CD) platform from Google Cloud that allows you to execute builds in Google Cloud.</p><p>To store the container image, we leverage Artifact Registry, a managed service from Google Cloud that provides secure and scalable repositories for software artifacts such as packages for Java, Node, or Python, but in this case, we will use it to store Docker images.</p><p>Below is a description of the steps and a high-level diagram showing the flow of the Cloud Build job:</p><ol><li>User triggers Cloud Build from local repository using Google Cloud commands.</li><li>Docker image is built from the current working directory.</li><li>Docker image is pushed to Artifact Registry.</li><li>Cloud Run instances are deployed from the Docker image.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*764-pHAQqbY5P5_0sMGapg.jpeg" /><figcaption><em>Figure 2: Cloud Build execution flow.</em></figcaption></figure><h3>A note about cost</h3><p>This article is tailored to a cloud engineer who likes to explore serverless offerings from Google Cloud. As such, a typical deployment of the application for noncommercial use should be covered under Google Cloud Free Program. You can visit the Google Cloud free-tier products page to learn more: <a href="https://cloud.google.com/free">https://cloud.google.com/free</a>. Following are the services and the related cost (as of September 2023) we will use for this tutorial:</p><ul><li><strong>Cloud Run</strong> free tier offers 2 million requests monthly, 360,000 GB-seconds of memory, 180,000 vCPU-seconds of compute time, and 1 GB network egress (North America only).</li><li><strong>Firestore </strong>free tier offers 1 GB storage per project and 50,000 reads, 20,000 writes, and 20,000 deletes per day, per project.</li><li><strong>API Gateway</strong> is not part of the free-tier offering, but it offers up to 2 million API calls per billing account per month for free.</li><li><strong>Cloud Build </strong>free tier offers 120 build minutes per day.</li><li><strong>Artifact Registry </strong>free tier offers 0.5 GB storage per month.</li></ul><p>Here, we will explore one example of a cost estimation for our serverless three-tier web application with example numbers. Actual costs will vary depending on usage.</p><p>We will use the following assumptions for our cost estimation:</p><ul><li>Cloud Run is used as a service, with CPU allocated only when running.</li><li>Cloud Run instances have 1vCPU and 512 MB memory.</li><li>There are 10,000 monthly users.</li><li>Each user averages 400 requests per month.</li><li>Each request takes 1,000 milliseconds to complete.</li><li>60 percent of requests are database reads.</li><li>40 percent of the requests are database writes.</li><li>Firestore has less than 15 GB of data.</li><li>All resources are deployed in the same region.</li></ul><p><strong>API Gateway:</strong></p><p>2 million to 1 billion requests per month = $3.00</p><p><strong>Firestore:</strong></p><p>15 GB storage = $2.52 per month</p><p><strong>Document reads:</strong></p><p>50,000</p><p>$0.036 per 100,000 documents</p><p><strong>Document writes:</strong></p><p>20,000</p><p>$0.108 per 100,000 documents</p><p>100 users * 40,000 requests per month = 4 million requests per month ≈ 2.4 million reads and 1.6 million writes per month</p><p>Read cost: 2.4 million reads / 30 ≈ 80,000 requests per day — 50,000 free requests = 30,000 paid requests per day = $0.036 per day for read requests * 30 = $1.08 per month</p><p>Write cost: 1.6 million writes / 30 ≈ 54,000 requests per day — 20,000 free requests = 34,000 paid requests per day = $0.216 per day for write requests * 30 = $6.48 per month</p><p><strong>Egress:</strong></p><p>Within same region = $0.00</p><p><strong>Cloud Run:</strong></p><p>CPU allocation time: 200,000 vCPU-second = $0.48</p><p>Memory allocation time: 100,000 GiB-second = $0.00</p><p>Number of requests: 4,000,000 = $0.80</p><p>Cloud Run total: $1.28 * 2 instances = $2.56 per month</p><p><strong>Monthly costs:</strong></p><p>API Gateway: $3.00</p><p>Firestore: $10.08</p><p>Egress: $0.00</p><p>Cloud Run: $2.56</p><p><strong>Total = $15.64 per month</strong></p><h3>Step-by-step guide</h3><h4><em>Setting up the Google Cloud project</em></h4><p>Projects are required to manage and organize resources in Google Cloud. We recommend creating a new project for this tutorial. This tutorial assumes that you have a Google Cloud project with admin (owner) access and you have access to Google Cloud Shell with either knowledge of command-line editors such as nano or vim or access to the Cloud Shell Editor. Cloud Shell Editor is a text editor built right into Cloud Shell and is a great option if you are not familiar with command-line editors.</p><p>To access your project in the Google Cloud console, navigate to the <a href="https://console.cloud.google.com/">Google Cloud console,</a> log in (or create an account), and select your project. If you do not already have a Google Cloud project, navigate to <strong>Menu</strong> &gt; <strong>IAM &amp; Admin</strong> &gt; <strong>Create a Project</strong> and follow the prompts in the console.</p><h4><em>Initial setup</em></h4><p>For simplicity, we use a single repository to hold all of the application, infrastructure, and pipeline code. For the initial setup, open a Cloud Shell session in the Google Cloud console for the project. Cloud Shell represents an internet-based platform for development and operational tasks and can be conveniently reached through a web browser. Google Cloud offers this service at no cost. Within this platform, you can oversee your assets using an online terminal that is equipped with various tools. For an in-depth dive of the preinstalled tools, please see <a href="https://cloud.google.com/shell/docs/how-cloud-shell-works#tools">https://cloud.google.com/shell/docs/how-cloud-shell-works#tools</a>.</p><p>You can open a new Cloud Shell session by visiting <a href="https://console.cloud.google.com/?cloudshell=true">https://console.cloud.google.com/?cloudshell=true</a>.</p><p>If not already updated, update Google Cloud Shell console to use your project:</p><pre>gcloud config set project [YOUR GOOGLE CLOUD PROJECT]<br><br>gcloud config list</pre><p><em>Step 1: Clone the GitHub repository</em></p><p>In the Cloud Shell session, git clone the reference application. You can clone this <a href="https://github.com/maksoodmohiuddin/google-cloud-serverless-app-pattern">repository</a> to start. However, we recommend you fork the repository and clone the forked repository. As part of this step-by-step guide, we will provide instructions on how to change the code to deploy the three-tier serverless application in your Google project.</p><pre>git clone [original https://github.com/maksoodmohiuddin/google-cloud-serverless-app-pattern.git or forked repo] cd google-cloud-serverless-app-pattern</pre><p>From here you can select “Open Editor” to launch the Cloud Shell editor.</p><p><em>Step 2: Update Google project ID and region</em></p><p>Next, let’s update <em>project_id</em> and <em>location</em> with the Google Cloud project you are using and a target Google Cloud region (such as “us-west2”) where you would like to deploy this application. Our example shows nano as an editor. However, you can choose to use another available editor that you prefer.</p><pre>cd infra<br>nano variables.tf<br><br>variable &quot;project_id&quot; {<br>  description = &quot;Project ID of the GCP project where resources will be deployed&quot;<br>  type        = string<br>  default     = &quot;PLEASE UPDATE WITH YOUR GOOGLE PROJECT ID&quot;<br>}<br><br>variable &quot;location&quot; {<br>  description = &quot;Location (region) where resources will be deployed&quot;<br>  type        = string<br>  default     = &quot;PLEASE UPDATE YOUR GOOGLE CLOUD REGION&quot;<br>}</pre><p>Please choose a region that supports Cloud Build, Cloud Run, Firebase, API Gateway, and Artifact Registry. For personal accounts, Cloud Build is restricted to certain regions. Therefore, if you are using a personal account, we recommend using one of the regions below:</p><ul><li>us-west2</li><li>asia-east1</li><li>australia-southeast1</li></ul><p>To learn more about Google Cloud regions and supported regions for the services for this app, please visit the following:</p><ul><li>General region picker: <a href="https://cloud.withgoogle.com/region-picker/">https://cloud.withgoogle.com/region-picker/</a></li><li>Regions that support Firestore: <a href="https://cloud.google.com/firestore/docs/locations">https://cloud.google.com/firestore/docs/locations</a></li><li>Regions that support Cloud Build: <a href="https://cloud.google.com/build/docs/locations">https://cloud.google.com/build/docs/locations</a></li><li>Regions that support Cloud Run: <a href="https://cloud.google.com/run/docs/locations">https://cloud.google.com/run/docs/locations</a></li><li>Regions that support Artifact Registry: <a href="https://cloud.google.com/artifact-registry/docs/repositories/repo-locations">https://cloud.google.com/artifact-registry/docs/repositories/repo-locations</a></li><li>Regions that support API Gateway: <a href="https://cloud.google.com/api-gateway/docs/deployment-model">https://cloud.google.com/api-gateway/docs/deployment-model</a></li></ul><h4><em>Deploying initial infrastructure</em></h4><p>In this section, we will deploy the initial infrastructure with Terraform. Due to dependency mapping that is not known to Terraform, we will need to target specific resources and set up the application sequentially.<br> <br>By default, Terraform stores state locally in a file named <em>terraform.tfstate</em>. For purposes of this tutorial, you can use the local state. However, if you prefer, state can be stored in remote state using Google Cloud Storage. Remote state is always preferred for projects with multiple developers. If you are especially savvy with Terraform, you may find it easier to create the bucket with Terraform local state, and then migrate the state into the bucket. See the Google Cloud <a href="https://cloud.google.com/docs/terraform/resource-management/store-state">documentation</a> for more details.</p><p><em>Step 1: Initialize Terraform</em></p><pre>terraform init</pre><p><em>Step 2: Validate Terraform plan</em></p><pre>terraform plan</pre><p>If prompted to authorize Cloud Shell, please select “Accept” to continue. This is because Cloud Shell needs permission to use your credentials for the Google Cloud command, and clicking “Authorize” grants permission to this and future calls.<br> <br> The output should be the following:</p><p>Plan: 18 to add, 0 to change, 0 to destroy</p><p><em>Step 3: Apply Terraform for the initial resources</em></p><pre>terraform apply</pre><p>When prompted, type “yes” to deploy the resources. It should match the plan.</p><h4><em>Deploying backend, API Gateway, and frontend</em></h4><p><em>Step 1: Update the backend Python Flask application code to use your Google project</em></p><p>Navigate to the backend folder and open the <em>firestore.py</em> file:</p><pre>cd ../app/backend/ nano -l firestore.py</pre><p>Update line 10 client with the Google Cloud project you are using:</p><pre>client = firestore.Client(project=&quot;PLEASE_UPDATE_PROJECT_ID&quot;)</pre><p><em>Step 2: Trigger Cloud Build to deploy backend</em></p><p>First, you can review the Cloud Build file and use the Google Cloud command line to deploy the backend Cloud Run based on the Cloud Build file:</p><pre>cat backend-cloudbuild.yaml gcloud builds submit --region=[YOUR GOOGLE CLOUD REGION] --config backend-cloudbuild.yaml e.g. gcloud builds submit --region=us-west2 --config backend-cloudbuild.yaml</pre><p>Note that the Cloud Build run can take several minutes to finish the run. This is expected. This Cloud Build file builds a Docker image for the backend service, pushes it to Artifact Registry, and then deploys the images from the Artifact Registry on a Cloud Run instance.</p><p><em>Step 3: Review the Swagger specification</em></p><p>We use API Gateway with OpenAPI Swagger specifications to connect the backend Cloud Run to API Gateway and set up the API Gateway configuration. Review the <em>api-gateway--espv2-definition.yml.tmpl</em> file under the infra folder.</p><p>First, use cd to go back to the infra directory and enable the <em>enable_api_gateway</em> flag to true:</p><pre>cd ../../infra cat api-gateway--espv2-definition.yml.tmpl</pre><p>You can learn more about OpenAPI specifications for API Gateway, Swagger, and Firebase as authentication for API Gateway by visiting the following:</p><p><a href="https://cloud.google.com/api-gateway/docs/openapi-overview">https://cloud.google.com/api-gateway/docs/openapi-overview</a></p><p><a href="https://swagger.io/">https://swagger.io</a></p><p><a href="https://cloud.google.com/api-gateway/docs/authenticating-users-firebase">https://cloud.google.com/api-gateway/docs/authenticating-users-firebase</a></p><p><em>Step 4: Deploy API Gateway</em></p><p>While still in the infra directory, enable the <em>enable_api_gateway</em> flag to true:</p><pre>nano variables.tf<br><br>variable &quot;enable_api_gateway&quot; { description = &quot;Feature flag to enable/disable API Gateway. Leverage this to deploy infra sequentially.&quot; type = bool default = true }</pre><p>Run the Terraform plan and then apply:</p><pre>terraform plan</pre><pre>terraform apply</pre><p>When prompted, type “yes” to deploy the resources. It should deploy three resources. Note that API Gateway typically takes longer to deploy than other resources. This is normal.</p><p><em>Step 5: Configure Firestore database via Firebase console</em></p><p>Navigate to <a href="https://console.firebase.google.com/">https://console.firebase.google.com/</a> and <strong>Create/Add Project &gt; Select your Google Cloud project from the dropdown &gt; Accept Terms &gt; Continue &gt; Confirm Plan &gt; Choose to enable Google Analytics or not &gt; Continue</strong>.<br> <br>In the landing page of your Firebase project, add your app as a web app to Firebase.<br> <br>Alternatively, under <strong>Project Overview</strong>, click on the gear icon, navigate to <strong>Project Settings</strong>, and register your app as a web app to Firebase.<br> <br>Register your app with your chosen name (for example, you can use your Google project name).<br> <br>Once your app is registered, under the SDK setup and configuration section (you can toggle to the configuration section), copy the value for <em>const firebaseConfig</em> and paste the value into <em>app/frontend/src/environments/environments.ts</em>.</p><p><strong>Warning:</strong> Make sure to not check in the values of <em>environments.ts</em> into a git repository because this is sensitive information.</p><pre>cd ../app/frontend/src/environments/ nano -l environment.ts<br><br>export const environment = { firebase: { apiKey: &quot;PLEASE UPDATE&quot;, authDomain: &quot;PLEASE UPDATE&quot;, projectId: &quot;PLEASE UPDATE&quot;, storageBucket: &quot;PLEASE UPDATE&quot;, messagingSenderId: &quot;PLEASE UPDATE&quot;, appId: &quot;PLEASE UPDATE&quot;, measurementId: &quot;PLEASE UPDATE&quot; // if measurement is enabled }, production: false };</pre><p>While still in Firebase console, enable Google as an authentication provider for Firebase with the following steps:<strong> All Products &gt; Authentication &gt; Get Started &gt; Google &gt; Enable &gt; Add Support Email &gt; Save.</strong></p><p><em>Step 6: Set the frontend API Gateway configuration</em></p><p>First, run the following command to get the <em>api-gateway </em>configuration:</p><pre>gcloud api-gateway gateways describe employee-gateway --location LOCATION --project PROJECT_ID --format &#39;value(defaultHostname)&#39;</pre><p>The output will look like the following: <em>employee-gateway-#.#.gateway.dev</em></p><p>Then update the three URLs (lines 13–15) in <em>app/frontend/src/employee/services/firestore.service.ts</em>; keep the path (for example, employee) as is.</p><pre>cd ../employee/services<br><br>nano -l firestore.service.ts<br><br>addEmployeeUrl = &#39;https://employee-gateway-#.#.gateway.dev/employee&#39;; employeesUrl = &#39;https://employee-gateway-#.#.gateway.dev/employees&#39;; deleteEmployeeUrl = &#39;https://employee-gateway-#.ue.gateway.dev/employee&#39;;</pre><p><em>Step 7: Trigger Cloud Build to deploy the frontend</em></p><p>Navigate to the frontend folder, review the Cloud Build file, and use the Google Cloud CLI to deploy the backend cloud instance.</p><pre>cd ../../.. cat frontend-cloudbuild.yaml gcloud builds submit --region=[YOUR GOOGLE CLOUD REGION]] --config frontend-cloudbuild.yaml e.g. gcloud builds submit --region=us-west2 --config frontend-cloudbuild.yaml</pre><p><em>Step 8: Update Firebase authorized domain with frontend Cloud Run URL</em></p><p>The frontend Cloud Run URL needs to be authorized for OAuth operations in the Firebase app.</p><p>You can use Google Cloud to get the Cloud Run URL:</p><pre>gcloud run services describe SERVICE --region REGION --format &#39;value(status.url)&#39;<br><br>e.g. gcloud run services describe amazing-employees-frontend-service --region us-west2 --format &#39;value(status.url)&#39;</pre><p>The output should be the following: https://amazing-employees-frontend-service-###.a.run.app</p><p>Copy that URL and add to the OAuth redirect domains list in the Firebase console.</p><p>Browse to <a href="https://console.firebase.google.com/">https://console.firebase.google.com/</a> and select your project.</p><p>Click on <strong>Authentication &gt; Settings Tab &gt; Authorized Domains &gt; Add Domain</strong>, and paste your frontend Cloud Run URL.</p><h4><em>Final step: Validate end-to-end app</em></h4><p>Browse to your frontend Cloud Run URL (https://amazing-employees-frontend-service-###.a.run.app), and use a Google account to log in to the app — your Google Cloud serverless application pattern using Cloud Run, API Gateway, and Firebase is up and running.</p><h4><em>Clean up</em></h4><p>We will not be able to run a Terraform destroy here due to API Gateway configuration dependencies.</p><p>To avoid incurring charges to your Google Cloud account for the resources used in this tutorial, you can delete the project.</p><p>Deleting a project has the following consequences:</p><ul><li>If you used an existing project, you’ll also delete any other work you’ve done in the project.</li><li>You can’t reuse the project ID of a deleted project. If you created a custom project ID that you plan to use in the future, delete the resources inside the project instead. This ensures that URLs that use the project ID, such as an appspot.com URL, remain available.</li></ul><p>To delete a project, do the following:</p><ul><li>In the Cloud console, go to the projects page (https://console.cloud.google.com/iam-admin/projects).</li><li>In the project list, select the project you want to delete and click “Delete.”</li><li>In the dialog, type the project ID and click “Shut down” to delete the project.</li></ul><h3>Summary</h3><p>This article delved into the deployment of a serverless three-tier application on Google Cloud. We explored how to use Cloud Run for both an Angular containerized frontend and a Python Flask containerized backend while leveraging Firestore for our database needs. Terraform and Cloud Build were highlighted for efficient infrastructure setup and deployments. For those eager to be hands-on, we provided a detailed step-by-step guide to ensure you can replicate and understand each phase of the deployment.</p><p>By understanding these key components and technologies, readers should be better poised to tackle serverless projects on Google Cloud.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=467d1a82369d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/digital-mckinsey/building-a-serverless-application-on-google-cloud-to-accelerate-deployment-time-with-reduced-cost-467d1a82369d">Building a serverless application on Google Cloud to accelerate deployment time with reduced cost…</a> was originally published in <a href="https://medium.com/digital-mckinsey">McKinsey Digital Insights</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Three Impediments to Overcome When Executing Digital Product Builds]]></title>
            <link>https://medium.com/digital-mckinsey/three-impediments-to-overcome-when-executing-digital-product-builds-24b779c39845?source=rss----9c56b966f13e---4</link>
            <guid isPermaLink="false">https://medium.com/p/24b779c39845</guid>
            <category><![CDATA[digital]]></category>
            <category><![CDATA[product]]></category>
            <category><![CDATA[product-management]]></category>
            <category><![CDATA[mckinsey]]></category>
            <dc:creator><![CDATA[McKinsey Digital]]></dc:creator>
            <pubDate>Thu, 14 Sep 2023 17:45:21 GMT</pubDate>
            <atom:updated>2023-09-15T20:55:08.047Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*1PhGwS6Nrpkui-VS" /><figcaption>Photo by <a href="https://unsplash.com/@epicantus?utm_source=medium&amp;utm_medium=referral">Daria Nepriakhina 🇺🇦</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p><strong>By Aditya Saxena — Expert Associate Partner, Deepak Joshi — Principal Product Manager, and Pallavi Misra — Product Specialist II, </strong><a href="https://www.mckinsey.com/capabilities/mckinsey-digital/how-we-help-clients/build-by-mckinsey/overview"><strong>Build by McKinsey</strong></a></p><p>Congratulations! The management team is impressed with your product vision and idea, and it has approved the budget to execute the idea. The idea is among the top three priorities for your organization this quarter, and the plan is to launch it in the market by next quarter. This is terrific and much-awaited news! Now starts your journey to convert your idea into reality. But here is a glaring fact that stares at you. According to research published in 2021¹, there are over 30,000 new products introduced every year (physical and digital), and 95% fail, telling us that converting product ideas into reality is no cakewalk. The impact of failed or delayed product builds isn’t only financial. There are significant non-monetary implications for teams and organizations, including decreased team morale and focus, diminished stakeholder confidence and interest, customer acquisition delays and their resultant poor feedback, and products falling behind the competition.</p><p>Enterprises are continuing to embrace tech and digital as core components of their business at an ever-increasing pace. And, as the investments in these areas increase, enterprises need to be ever more cautious to drive the desired results from these investments. In this article, we will share our learnings from helping multiple clients in their journey to build successful digital products and businesses.</p><p><strong>Three impediments we have seen over and over again while executing Product Builds:</strong></p><p>Being aware of these three impediments will help the owners of digital products at enterprises, from product leaders and product managers to delivery leaders and product owners, to avoid the most common traps which can lead to inefficient digital builds and proactively find ways to mitigate them. Let’s dive in.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/605/1*8ACeOcLw3qFc42kc_tjR_Q.png" /></figure><p>1. <strong>Unclear requirements:</strong> This is one of the most common causes for delays which affect the success of a product launch. Poor clarity on requirements can sprout from multiple sources, including:</p><ul><li><strong>Product vision clarity:</strong> If not articulated, socialized, or aligned with stakeholders, this can lead to frequent conflicts and misunderstandings on scope and context. To mitigate this, share and discuss the overall product vision whenever a new team member/stakeholder is onboarded. The product vision can be articulated through various methods, as simple as filling a future press release template or product canvas. A strong sign that the vision is understood comes when team members ask questions such as — ‘How do you think this feature helps us achieve our vision?’</li><li><strong>Functional requirement — quality assurance and management:</strong> Poor quality requirements often include unqualified assumptions and incorrect or insufficient details needed to execute a requirement. This leads to a lot of rework and developer frustration. A Definition of Ready (DoR) could help mitigate this. The entire team should align on a DoR that should be met before development begins, including everything from the format of requirement (Gherkin vs. Regular) and design readiness to developer input and stakeholder sign-off. Sometimes, different developers are working on multiple versions of the same requirement. Do you get questions from your development team such as — “But Figma shows this user journey, so that’s how I have implemented it!” To handle this, you should have a single source of truth using a backlog management tool that suits your requirements and set up processes to ensure that changes are curtailed and clearly communicated. These are just some good practices to ensure that the requirements are aligned before the development team starts on that piece of coding and the teams should feel free to find ways that help them stay aligned.</li><li><strong>Technical requirements’ consideration:</strong> It is key to consider and manage a product’s technical requirements to avoid any unpleasant surprises. There are two key aspects here. The first is quality, including code quality, security aspects, performance, etc. The likes of coverage and code smells for the quality aspects should be defined and agreed upon in a ‘Definition of done’ with the stakeholders. Any story should be marked as done only if it meets all the quality bars. The second aspect is to ensure a technical runway supporting the development of the functional elements. These tech requirements often involve multiple teams so should be replicated and prioritized across the backlogs of other teams. Time should also be built in to convince and influence these other team members (more on this in later articles).</li></ul><p>2. <strong>Poor planning:</strong> The lack of a plan is the beginning of software development going wrong and is akin to accepting failure. As they say, fail to prepare, prepare to fail. Irrespective of the uncertainties and moving parts in the product development process, planning is essential to ensure that all stakeholders and team members know what they are doing, when they are supposed to do it, and who is responsible for what. Some key aspects to consider while planning includes the following:</p><ul><li><strong>Iterative Planning:</strong> When your leadership asks, ‘When can we go live with this?’ the answer should not be, ‘Development will be complete on this date, marketing will then produce a plan on this, and then…’. You should have a clear, up-to-date, comprehensive plan to share with leadership regularly and ad-hoc. A plan should not be limited to development sprints; it should cut through all key workstreams/functions needed to make the product live i.e., customer service setup, marketing communication, design, etc. By comprehensive planning, we do not mean trying to have every detail about the product requirement before starting. In fact, most products inevitably start from unclear requirements, but having a plan and vision is a must-have so that your team is anchored to the tasks and timelines and continues to iterate on them regularly.</li><li><strong>Metric-driven predictability</strong>: Product and program managers should be able to identify potential timeline deviations and plan based on team capacity using metrics and methods like burndown charts, velocity, and capacity. Metrics should be available for the leadership and team to view as needed and should be discussed in Sprint review.</li><li><strong>Launch strategy and success metrics:</strong> Defining and implementing these success metrics is essential for continuously striving for product-market fit. The development should ensure the plan and requirements are geared towards achieving the defined metrics. This includes determining success in terms of key metrics when the product/feature is launched, in the short-term and the long term e.g., adoption, activation, conversion, bug reports, customer reviews etc. We can imagine some agile advocates jumping off their chairs and suggesting that this level of planning is anti-agile. We believe that planning (if it is done with the correct input from various dependent teams) does not need to delay the execution. The right amount of planning and the right stakeholders can significantly de-risk execution and timelines, allowing to surface gaps in the plan early on.</li></ul><p>3. <strong>Siloed operating model:</strong> While the agile mindset and Scrum or Kanban form the backbone of the processes, some process areas require deliberate effort to keep the product development process smooth and enable the team members to function effectively. It’s vitally important to consider each of the following to ensure the best possible chance of success for your next product development cycle.</p><ul><li><strong>Co-creating with users:</strong> Imagine only receiving user feedback at the end of a build cycle as a part of a standalone UAT (user acceptance testing) and hearing ‘This is not what I meant when I said….’ Not only would this be hugely frustrating after a potentially long and drawn-out development cycle, but this can lead to significant delays, as ignoring these inputs can lead to low adoption later. Mechanisms should be set up to obtain and incorporate user inputs regularly, so they are baked into the sprint cadence from sprint 1. This can be achieved by creating a small panel with existing or potential users. This panel can be invited to demos, and immersion sessions each sprint to get the user’s thoughts on a regular basis.</li><li><strong>Cross-functional, goal-focused collaboration:</strong> Team mindset becomes critically important in the setup where new teams are formed to deliver on a mission. It is important for the team to establish a common ground concerning individual working styles, roles, responsibilities, learning goals, and aspirations, with the focus being on the mission that they are set together to achieve. A mechanism should be set up to obtain and incorporate inputs from all related teams/departments (marketing/finance/legal) regularly so that their insights are baked into the product right from the start. For example: Inviting members from different departments during the demos to represent their teams and getting their feedback on what is already built and the upcoming planned implementation.</li><li><strong>Continuous improvement:</strong> In the rush to deliver, sometimes the teams skip ceremonies like retrospectives, which enable them to pause, reflect and learn from their mistakes and achievements and accordingly adapt the operating model. Actions should be identified out of each retrospective, along with owners for each action, and the action items should be part of a sprint’s plan and objectives. And all this can be done while keeping the retrospectives fun.</li></ul><p>While the context of all organizations and digital builds may differ, identifying and mitigating these impediments early will help you ensure that the digital build goes as planned in delivering the desired outcomes. This will help you save not only monetary costs but also the confidence of your team, stakeholders, and customers.</p><p>In addition to being aware of these three common impediments and their mitigation strategies, here are a few key skills to have as a Product Manager for successful product builds: <a href="https://www.mckinsey.com/industries/technology-media-and-telecommunications/our-insights/what-separates-top-product-managers-from-the-rest-of-the-pack">What separates top product managers from the rest of the pack?</a>. We look forward to sharing our learnings from our experience in helping companies with their product builds in a series of articles such as below:</p><p>1. Holistic value maximization in product builds.</p><p>2. Team aspects of a Product build.</p><p>3. Web3 architecture layers and choices.</p><p>4. Single vs. multi-app strategy — Imperatives and Implications.</p><p>5. Gen AI toolkit for Product Owners</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/605/1*ZFtDlUPnigFICn3HNFV0Kg.png" /></figure><p>Please do share with us your thoughts and experience on how you have mitigated some of the challenges we have outlined above in your product builds.</p><p>Reference:</p><p>¹<a href="https://professionalprograms.mit.edu/blog/design/why-95-of-new-products-miss-the-mark-and-how-yours-can-avoid-the-same-fate/">https://professionalprograms.mit.edu/blog/design/why-95-of-new-products-miss-the-mark-and-how-yours-can-avoid-the-same-fate/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=24b779c39845" width="1" height="1" alt=""><hr><p><a href="https://medium.com/digital-mckinsey/three-impediments-to-overcome-when-executing-digital-product-builds-24b779c39845">Three Impediments to Overcome When Executing Digital Product Builds</a> was originally published in <a href="https://medium.com/digital-mckinsey">McKinsey Digital Insights</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Toward end-to-end real-time event-driven architectures using GraphQL with AWS AppSync]]></title>
            <link>https://medium.com/digital-mckinsey/toward-end-to-end-real-time-event-driven-architectures-using-graphql-with-aws-appsync-a588ef7b8c90?source=rss----9c56b966f13e---4</link>
            <guid isPermaLink="false">https://medium.com/p/a588ef7b8c90</guid>
            <category><![CDATA[graphql]]></category>
            <category><![CDATA[mckinsey]]></category>
            <category><![CDATA[cloud-architecture]]></category>
            <category><![CDATA[cloud-computing]]></category>
            <category><![CDATA[aws-appsync]]></category>
            <dc:creator><![CDATA[McKinsey Digital]]></dc:creator>
            <pubDate>Fri, 28 Jul 2023 15:43:04 GMT</pubDate>
            <atom:updated>2023-09-12T17:40:15.677Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DNVBTw9RTj9oPfppjQmZYg.jpeg" /></figure><p><strong>By Bent Christensen, Principal Cloud Engineer I, </strong><a href="https://www.mckinsey.com/capabilities/mckinsey-digital/cloud/overview"><strong>Cloud by McKinsey</strong></a></p><p>This article explores how GraphQL can be used to implement a fully event-driven cloud architecture — in the sense that it is comprised of decoupled scalable services in the backend but also extends to the frontend where events can be received in a pub/sub manner. This avoids a clunky and wasteful polling mechanism or, worse, the need for constant refreshes by the end user to get the latest updates.</p><p>Both event-driven architectures and GraphQL are trending technology approaches in modern application development, emerging from different domains and for different reasons, which makes the combination of the two an interesting experiment and uncharted territory.</p><ul><li>An <a href="https://aws.amazon.com/what-is/eda/">event-driven architecture</a> (EDA) is a pattern that promotes <a href="https://aws.amazon.com/blogs/compute/getting-started-with-event-driven-architecture/">developing small, decoupled services</a> that publish, consume, or route events. This makes it easier to scale, update, and independently deploy separate components of the system.</li><li><a href="https://aws.amazon.com/appsync/product-details/">GraphQL</a> is an API query language for frontend applications meant to replace traditional REST APIs. GraphQL brings a type system to APIs, a declarative query language to avoid over- and under-fetching, real-time updates, and a mechanism to consolidate multiple data sources into a single, unified data-access endpoint. This provides a better and more consistent frontend and backend developer experience through a <a href="https://www.apollographql.com/docs/federation/#:~:text=Separation%20of%20concerns,without%20interfering%20with%20each%20other.">separation of concerns approach</a>.</li></ul><p>The reason for exploring the combination of an EDA and GraphQL is to create a reusable recipe or application pattern that combines the benefits of both technologies in a coherent single approach for modern application development.</p><p>The infrastructure implementation for this article is available as Terraform using native services in AWS and a React app for the frontend application.</p><h3><strong>Architecture overview</strong></h3><p>The proposed infrastructure implementation is shown in Figure 1, where a frontend React app can send queries to a GraphQL endpoint provided by AWS AppSync. When the processing has finished, the React app will receive the results asynchronously and in real time through a GraphQL subscription connection.</p><p>Notice how the initial query only returns an acknowledgment for accepting the request, but the result of the request is delivered out of band through a separate mechanism.</p><p>Behind the GraphQL endpoint is a basic EDA that uses an AWS SQS to queue incoming requests, and then an AWS Lambda function will take requests off the queue and start processing, before publishing the result on an EventBridge bus. At this point, a rule will be triggered to invoke a mutation on the same AWS AppSync API, which can be subscribed to by the React app, in order to receive the final result in real time.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/624/1*q_Jl7Hn6De8TsiX8pK1PmQ.png" /></figure><p><em>Figure 1: GraphQL endpoint provided by AppSync in front of a basic event-driven backend.</em></p><h3><strong>Walk-through</strong></h3><p>The walk-through of each of the infrastructure components follows the flow of an event initiated by a React app end user (the left side of Figure 1) all the way through the architecture and back to the React app.</p><h4><strong>End user React app</strong></h4><p>One of the most popular frontend libraries is React, and for this particular exploration of GraphQL, the <a href="https://www.apollographql.com/docs/react/">Apollo Client</a> v3 library was chosen. This was due to its ability to integrate seamlessly into the React component structure and because it has a rich set of configurable features, such as authorization and state management.</p><p>Besides the core data fetching and data modifying mechanisms, the Apollo Client v3 has an advanced state management functionality that can support an offline-first approach. This contributes to keeping the frontend source code consistent and avoids the need for an additional state management layer or library such as Redux.</p><p>Alternatives to Apollo Client v3 include the following:</p><ul><li><strong>aws-amplify/api</strong> is part of the AWS Amplify ecosystem and provides basic support for queries, mutations, and subscriptions provided by AppSync. It’s a good choice if you are already using the Amplify libraries in your app.</li><li><strong>URQL</strong> is a streamlined minimalistic library that is highly extensible and can be used with a variety of JavaScript libraries — not only React but also plain JavaScript and node.js, for example.</li><li><strong>Relay</strong> is built for scaling and optimization but is also highly opinionated and has a steeper learning curve compared to Apollo Client.</li></ul><p>The configuration of the Apollo Client library in the React app can be seen in Figure 2. It is a bare-bones configuration with in-memory cache and uses the AppSync API key for authentication. Most real-world applications would use JSON web tokens (JWT) with appropriate claims, such as role and tenant, to authenticate and authorize the current user within the AppSync resolvers. But this simpler configuration is perfect to showcase the event-driven flow in a development environment without too much overhead.</p><p>Also notice the imported packages <em>aws-appsync-auth-link</em> and <em>aws-appsync-subscription-link</em>, which are provided by AWS to <a href="https://github.com/awslabs/aws-mobile-appsync-sdk-js#using-authorization-and-subscription-links-with-apollo-client-v3-no-offline-support">integrate the Apollo Client with AppSync</a>.</p><pre>import { ApolloClient, InMemoryCache, ApolloLink, createHttpLink } from &#39;@apollo/client&#39;<br>import { createAuthLink } from &#39;aws-appsync-auth-link&#39;<br>import { createSubscriptionHandshakeLink } from &#39;aws-appsync-subscription-link&#39;<br><br>const url = process.env[&#39;REACT_APP_APPSYNC_ENDPOINT&#39;] || &#39;&#39;<br>const region = process.env[&#39;REACT_APP_APPSYNC_REGION&#39;] || &#39;&#39;<br><br>const auth: any = {<br>  type: &#39;API_KEY&#39;,<br>  apiKey: process.env[&#39;REACT_APP_APPSYNC_API_KEY&#39;] || &#39;&#39;<br>  // jwtToken: async () =&gt; token, // Used for Cognito UserPools OR OpenID Connect.<br>}<br><br>const httpLink = createHttpLink({ uri: url })<br><br>const link = ApolloLink.from([<br>  createAuthLink({ url, region, auth }),<br>  createSubscriptionHandshakeLink({ url, region, auth }, httpLink)<br>])<br><br>export const getClient = () =&gt; {<br>  const client = new ApolloClient({<br>    link,<br>    cache: new InMemoryCache()<br>  })<br><br>  return client<br>}</pre><p><em>Figure 2: React app GraphQL configuration.</em></p><h4><strong>GraphQL API with AppSync</strong></h4><p>The AppSync configuration supports two flows, namely the incoming initial event from the end-user React app and then a pub/sub style event dispatcher communicating the result back to the React app through a subscription. Let’s start with the simpler one — the configuration of handling the incoming event.</p><p><em>Handling incoming events with AppSync</em></p><p>In the GraphQL schema included in Figure 3, a simple query called <em>sendRequest</em> is defined to handle the incoming event. This could also be a mutation if the request involves changes to the state of the data structure in the backend. The <em>sendRequest</em> query takes an integer as input through the variable data that is used in the backend calculation and a channel name that is used to publish the result to the correct end users.</p><p>The immediate result from making a <em>sendRequest</em> query is a <em>RequestResponse</em> object containing the status, which can be true or false according to whether or not the <em>sendRequest</em> query was received correctly. Following this, a <em>messageId</em>, which is the tracking ID, can be used to match the correct reply event in case multiple <em>sendRequest</em> queries are performed close together. The <em>ResponseObject</em> contains additional fields for debugging purposes, but these are not used here.</p><pre>type Result {<br>  data: Int<br>  channel: String<br>  messageId: String<br>}<br><br>type Mutation {<br>  pubMsg(data: Int!, channel: String!, messageId: String!): Result<br>}<br><br>type Query {<br>  sendRequest(data: Int!, channel: String!): RequestResponse<br>}<br><br>type RequestResponse {<br>  status: Boolean<br>  SendMessageResponse: TSendMessageResponse<br>  messageId: String<br>}<br><br>type TSendMessageResponse {<br>  xmlns: String<br>  SendMessageResult: TSendMessageResult<br>  ResponseMetadata: TResponseMetadata<br>}<br><br>type TSendMessageResult {<br>  MD5OfMessageBody: String<br>  MessageId: String<br>}<br><br>type TResponseMetadata {<br>  RequestId: String<br>}<br><br>type Subscription {<br>  subMsg(channel: String!): Result @aws_subscribe(mutations: [&quot;pubMsg&quot;])<br>}<br><br>schema {<br>  query: Query<br>  mutation: Mutation<br>  subscription: Subscription<br>}</pre><p><em>Figure 3: GraphQL API schema for the event-driven architecture.</em></p><p>The resolver behind the <em>sendRequest</em> query field is taking the data input and placing it on an SQS queue for further decoupled processing (more on this in the next section). The resolver is shown in Figure 4.</p><p>Notice how there is no intermediate glue-code Lambda function, but the JavaScript resolver is interacting directly with the queue through the HTTP API by building a HTTP POST request body from the data and channel input parameters in the request template. And then in the response template, it is relaying the result from adding the message on the queue and reusing the SQS generated <em>messageId</em> as the tracking ID for the event throughout the infrastructure.</p><pre>import { util } from &quot;@aws-appsync/utils&quot;<br><br>export function request(ctx) {<br>  const { args: { data, channel },<br>        stash: { sqs_queue_url },<br>  } = ctx;<br>  const MessageBody = util.urlEncode(JSON.stringify({ data, channel }));<br>  return {<br>    method: &quot;POST&quot;,<br>    resourcePath: &quot;/&quot;,<br>    params: {<br>      headers: { &quot;Content-Type&quot;: &quot;application/x-www-form-urlencoded&quot; },<br>      body: `Action=SendMessage&amp;Version=2012-11-05&amp;QueueUrl=${sqs_queue_url}&amp;MessageBody=${MessageBody}`,<br>    },<br>  };<br>}<br><br>export function response(ctx) {<br>  const { result: { statusCode, body }} = ctx;<br><br>  const resultMap = {};<br>  if (statusCode === 200) {<br>    resultMap[&quot;status&quot;] = true;<br>    resultMap[&quot;SendMessageResponse&quot;] = util.xml.toMap(body).SendMessageResponse;<br>    resultMap[&quot;messageId&quot;] =<br>      util.xml.toMap(body).SendMessageResponse.SendMessageResult.MessageId;<br>  } else {<br>    resultMap[&quot;status&quot;] = false;<br>  }<br>  return resultMap;<br>}</pre><p><em>Figure 4: JavaScript Resolver for the sendRequest query field.</em></p><p>The Terraform configuration of the JavaScript resolver from before is displayed in Figure 5, where the file <em>add_to_sqs_resolver.js</em> is listed in Figure 4. All resolvers using the <em>APPSYNC_JS</em> runtime must be of type <em>pipeline</em> resolver instead of just the <em>unit</em> resolver type.</p><p>Notice how the URL of the SQS queue is injected into <em>ctx.stash</em> by Terraform, so other resolvers in the same pipeline can fetch it easily as an environment variable.</p><pre>resource &quot;aws_appsync_resolver&quot; &quot;add_to_sqs_resolver&quot; {<br>  type   = &quot;Query&quot;<br>  api_id = aws_appsync_graphql_api.this.id<br>  field  = &quot;sendRequest&quot;<br>  kind   = &quot;PIPELINE&quot;<br>  code   = &lt;&lt;EOF<br>    export function request(ctx) {<br>      ctx.stash.sqs_queue_url = &#39;${aws_sqs_queue.this.url}&#39;<br>      return {}<br>    }<br>    export function response(ctx) {<br>      return ctx.prev.result;<br>    }<br>EOF<br><br>  runtime {<br>    name            = &quot;APPSYNC_JS&quot;<br>    runtime_version = &quot;1.0.0&quot;<br>  }<br><br>  pipeline_config {<br>    functions = [ aws_appsync_function.add_to_sqs_function.function_id ]<br>  }<br>}<br><br>resource &quot;aws_appsync_function&quot; &quot;add_to_sqs_function&quot; {<br>  api_id      = aws_appsync_graphql_api.this.id<br>  data_source = aws_appsync_datasource.queue.name<br>  name        = &quot;add_to_sqs_event_queue&quot;<br>  code        = file(&quot;./add_to_sqs_resolver.js&quot;)<br><br>  runtime {<br>    name            = &quot;APPSYNC_JS&quot;<br>    runtime_version = &quot;1.0.0&quot;<br>  }<br>}</pre><p><em>Figure 5: Terraform configuration of the JavaScript pipeline resolver for the sendRequest field.</em></p><p><em>Handling outgoing events with AppSync</em></p><p>Once the calculations in the backend have been successfully performed, you might want to output the result through the AppSync API.</p><p>A mechanism to accomplish this pattern is to create a <a href="https://docs.aws.amazon.com/appsync/latest/devguide/aws-appsync-real-time-create-generic-api-serverless-websocket.html">pub/sub style API</a> with a mutation as the publisher and a subscription listening to the mutation as the subscriber, as seen in Figure 6. The mutation is only meant to be called by an internal cloud part of the infrastructure, with the result as a parameter, and the subscription is to be used by the React app to receive the result in real time through an established connection. So, in the React app, before sending any queries to the AppSync <em>sendRequest</em> field, a subscription should be created and established to receive any results of the request.</p><p>In the GraphQL schema in Figure 3, the corresponding mutation is called <em>pubMsg</em> and the subscription is called <em>subMsg</em>. The <em>pubMsg</em> mutation takes <em>data</em>, <em>channel</em>, and <em>messageId</em> as parameters, where <em>data</em> is the actual result we want from the request, <em>channel</em> is our filter to ensure only relevant end users receive the result, and, finally, the <em>messageId</em> matches the received result with a <em>sendRequest</em> query within the React app. Our internal part of the infrastructure (big reveal — the EventBridge) will provide these parameters to the <em>pubMsg</em> mutation (detailed later in the section “Relay results with EventBridge”), and the same parameters will automatically also be broadcasted over any established subscriptions meeting certain criteria.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/624/1*0-DAHzK8T5LKublmNOHPug.png" /></figure><p><em>Figure 6: Detailed AppSync event flow.</em></p><p>The Terraform configuration as seen in Figure 7 for the <em>pubMsg</em> resolver is straightforward, since it is used only to relay information and not to perform any computation or transformation. And the subscription <em>subMsg</em> field doesn’t even have any Terraform configuration but is using AppSync default resolvers. From the schema in Figure 4, we can see that the <em>subMsg</em> subscription takes one input parameter when the React app creates a new connection, and that is the channel name.</p><p>Notice the <a href="https://docs.aws.amazon.com/appsync/latest/devguide/aws-appsync-real-time-data.html#using-subscription-arguments">not null requirement</a> with the <em>String!</em> type on the channel parameter. This is specific to AppSync and means that before sending anything to listening applications, the result is filtered by requiring a matching value in a field with the name channel in the result object to be sent. Without the <em>!</em> at the end of the String type in the schema, a React application subscribing without providing a channel name would receive all results, including the results meant for other end users. This is not ideal if any of the results are confidential, but it is also just bad practice to over-fetch data that is not needed. So, filtering by a channel or topic before sending the results over the internet is preferred. In cases where sharing of the results is intended, a pre-shared channel name could be used in the example app, so groups of end users would receive the results whenever one of them makes a new <em>sendRequest</em> query.</p><p>For the example prototype developed for this article, a pre-shared channel name works fine to showcase the basic functionality of filtering, but for real-world applications, it is best practice to implement filtering for private sessions on unique end user information such as userId, role, or tenantId claims in a JWT.</p><pre>resource &quot;aws_appsync_resolver&quot; &quot;pubMsg_resolver&quot; {<br>  type   = &quot;Mutation&quot;<br>  api_id = aws_appsync_graphql_api.this.id<br>  field  = &quot;pubMsg&quot;<br>  kind   = &quot;PIPELINE&quot;<br>  code   = &lt;&lt;EOF<br>    export function request(ctx) {<br>      return {}<br>    }<br>    export function response(ctx) {<br>      return ctx.prev.result;<br>    }<br>EOF<br>  runtime {<br>    name            = &quot;APPSYNC_JS&quot;<br>    runtime_version = &quot;1.0.0&quot;<br>  }<br><br>  pipeline_config {<br>    functions = [ aws_appsync_function.passthrough.function_id ]<br>  }<br>}<br><br>resource &quot;aws_appsync_function&quot; &quot;passthrough&quot; {<br>  api_id      = aws_appsync_graphql_api.this.id<br>  data_source = aws_appsync_datasource.none.name<br>  name        = &quot;passthrough&quot;<br>  code        = &lt;&lt;EOF<br>    export function request(ctx) {<br>     return { payload: ctx.args }<br>    }<br>    export function response(ctx) {<br>      return ctx.result;<br>    }  <br>EOF<br><br>  runtime {<br>    name            = &quot;APPSYNC_JS&quot;<br>    runtime_version = &quot;1.0.0&quot;<br>  }<br>}</pre><p><em>Figure 7: Resolver for the pubMsg mutation.</em></p><h4><em>Queuing incoming events with SQS</em></h4><p>The queue for incoming events is following a standard EDA pattern as outlined in the AWS-provided <a href="https://aws.amazon.com/what-is/eda/">overview of EDA</a>.</p><p>As seen in Figure 8, the definition of the event queue itself is simple. I have included the triggering mechanism as well for Lambda to pick up one item from the queue when a new message is added. This is convenient for the example prototype, but in a real-world situation, the processing component of an event-driven architecture might be an on-premises legacy system that is capable of processing a message only every ten minutes. If that’s the case, then the queue will serve its main purpose as part of the architecture and act as a buffer, and the mechanism for consuming messages from the queue will be a pull-based instead of a push-based trigger, as shown in Figure 7.</p><p>Further best-practice configurations of the event queue in a real-world scenario include the following:</p><ul><li>Add a dead-letter queue for messages that failed to be processed.</li><li>Use multiple queues to prioritize the order of message processing.</li><li>Optimize consumption of messages from the queue by consuming batches instead of just one message at a time if supported by backend processing.</li></ul><pre>### Sqs ###<br>resource &quot;aws_sqs_queue&quot; &quot;this&quot; {<br>  name = &quot;event_queue&quot;<br>}<br><br>### Lambda to be triggered by sqs ###<br>resource &quot;aws_lambda_event_source_mapping&quot; &quot;this&quot; {<br>  batch_size       = 1<br>  event_source_arn = aws_sqs_queue.this.arn<br>  enabled          = true<br>  function_name    = aws_lambda_function.processing_lambda.arn<br>}</pre><p><em>Figure 8: Configuring incoming event queue with SQS.</em></p><p>Since this article is focused on the GraphQL frontend, I wanted to share the configuration seen in Figure 9 of the SQS queue as a DataSource in the AppSync API definition, as this is not an obvious configuration. Notice how the DataSource is of the generic HTTP type, since SQS queues are not directly supported as a DataSource type in AppSync, as are other AWS services such as DynamoDB, Lambda, or RDS.</p><pre>resource &quot;aws_appsync_datasource&quot; &quot;queue&quot; {<br>  api_id           = aws_appsync_graphql_api.this.id<br>  name             = &quot;sqs_event_queue&quot;<br>  service_role_arn = aws_iam_role.appsync_datasource_role.arn<br>  type             = &quot;HTTP&quot;<br><br>  http_config {<br>    endpoint = &quot;https://sqs.${var.region}.amazonaws.com/&quot;<br>    authorization_config {<br>      authorization_type = &quot;AWS_IAM&quot;<br>      aws_iam_config {<br>        signing_region       = var.region<br>        signing_service_name = &quot;sqs&quot;<br>      }<br>    }<br>  }<br>}</pre><p><em>Figure 9: SQS queue as AppSync DataSource.</em></p><p>This also means that the authorization mechanism must be explicitly configured from outside the AWS console user interface, where there are no fields for specifying IAM roles or policies for accessing the HTTP endpoint, as illustrated in Figure 10.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/898/1*Bk7Clvrtv1RKB7mwxR7g2Q.png" /></figure><p><em>Figure 10: Adding a new HTTP DataSource for AppSync.</em></p><p>So, for the AppSync resolvers to have enough permissions to add new messages to the SQS queue, a new role that can be assumed by the AppSync service must be created and a policy must be attached to this role, allowing the <em>sqs:SendMessage</em> action as shown in Figure 11.</p><pre>resource &quot;aws_iam_role_policy&quot; &quot;appsync_datasource_role_policy&quot; {<br>  name = &quot;appsync_datasource_role_policy&quot;<br>  role = aws_iam_role.appsync_datasource_role.id<br><br>  policy = &lt;&lt;EOF<br>{<br>  &quot;Version&quot;: &quot;2012-10-17&quot;,<br>  &quot;Statement&quot;: [<br>    {<br>      &quot;Action&quot;: [<br>        &quot;sqs:SendMessage&quot;<br>      ],<br>      &quot;Effect&quot;: &quot;Allow&quot;,<br>      &quot;Resource&quot;: &quot;${aws_sqs_queue.this.arn}&quot;<br>    }<br>  ]<br>}<br>EOF<br>}</pre><p><em>Figure 11: AppSync DataSource policy for adding a message to SQS queue.</em></p><h4><em>Processing with Lambda</em></h4><p>The actual processing component of the event-driven architecture used here to explore GraphQL frontend interaction is merely a placeholder for a possibly complex and sophisticated computation or interaction with an on-premises system. In a real-world situation, the processing component ideally should not be constrained in an event-driven architecture to reply within a certain time limit, such as a HTTP timeout of the browser, but instead be decoupled and publish the reply to any interested subscribers.</p><p>The processing with Lambda in the example provided here is simply taking an integer as input from the SQS queue when triggered and then calculating the squared value before publishing the result to EventBridge, as seen in Figure 12.</p><p>The <em>messageId</em> field generated by the SQS queue when the event was first received from the React app and returned immediately to be used as the tracking ID of the request is now pulled out of the input event coming from SQS to the processing Lambda and is injected back into the <em>Detail</em> field of the message before being published on EventBridge. This allows the React app to track and match the <em>messageId</em> value received in the final result message with the initial <em>sendRequest</em> tracking ID received earlier.</p><pre>const AWSXRay = require(&#39;aws-xray-sdk-core&#39;)<br>const AWS = AWSXRay.captureAWS(require(&#39;aws-sdk&#39;))<br>AWS.config.update({ region: process.env.APPSYNC_REGION })<br><br>const eb = new AWS.EventBridge()<br><br>const legacy_computation = (input) =&gt; {<br>  // return the value squared...<br>  return input * input  <br>}<br><br>exports.handler = async (event) =&gt; {<br>    // Response object<br>    const res = { data: 0, channel: &#39;&#39;, messageId: &#39;&#39; }<br><br>    // Take the next item on the event_queue<br>    if (event.Records &amp;&amp; event.Records[0].body &amp;&amp; event.Records[0].body.length &gt; 0) {<br>        const req_args = JSON.parse(event.Records[0].body)  <br><br>        // Perform some time-consuming computation or interaction with other systems.<br>        res.data = legacy_computation(req_args.data)<br>        res.channel = req_args.channel<br>        res.messageId = event.Records[0].messageId<br>    }<br><br>    // Publish the result on eventBridge<br>    const params = {<br>        Entries: [{<br>            Detail: JSON.stringify(res),<br>            DetailType: &#39;gql-eda-poc&#39;,<br>            Source: &#39;com.ticketdispenser.gql-eda-poc&#39;,<br>            EventBusName: process.env.EVENTBUS_NAME<br>        }]<br>    };<br><br>    const log_result = await eb.putEvents(params).promise()<br>}</pre><p><em>Figure 12: Processing Lambda publishing reply on EventBridge.</em></p><h4><em>Relaying results with EventBridge</em></h4><p>The heart of any event-driven architecture usually contains an event router or a pub/sub broker where events can be relayed between the decoupled architecture components. For this prototype, EventBridge is used as the event router for the result of the processing Lambda described in the previous section.</p><p>Creating the EventBridge resource is the easy part, as seen in Figure 13, where it consists of three lines of configuration that provide a name for the EventBridge resource as the only parameter. And then an EventBridge rule is added to match messages sent from the processing Lambda by the content of the <em>DetailType</em> field of the message (<em>gql-eda-poc</em>).</p><pre>### EventBridge ###<br>resource &quot;aws_cloudwatch_event_bus&quot; &quot;this&quot; {<br>  name = &quot;gql-eda-poc-messages&quot;<br>}<br><br>### EventBridge rule ###<br>resource &quot;aws_cloudwatch_event_rule&quot; &quot;this&quot; {<br>  name           = &quot;send-to-gql&quot;<br>  description    = &quot;Send event to appSync&quot;<br>  event_bus_name = aws_cloudwatch_event_bus.this.name<br><br>  event_pattern = &lt;&lt;EOF<br>{<br>  &quot;detail-type&quot;: [&quot;gql-eda-poc&quot;]<br>}<br>EOF<br>}</pre><p><em>Figure 13: EventBridge and API destination configuration.</em></p><p>The next step in the event flow is to return the message back to the <em>pubMsg</em> mutation in the AppSync API, so the subscription <em>subMsg</em> will send out a message to the React app and the flow is complete. EventBridge does not yet support AppSync as a native AWS service target for EventBridge rules, so, instead, the generic HTTP API destination target type can be used, as shown in Figure 14. The authentication for AppSync also needs to be manually configured by adding the AppSync API KEY to the header of the HTTP POST request made from the EventBridge service to the AppSync service.</p><p>Alternatively, a Lambda function could be used as an intermediary step, since Lambda is supported natively as a EventBridge target, and then the Lambda function could turn around and call the AppSync <em>pubMsg</em> using the standard AWS SDK library. This would work, and in some cases might be necessary, such as when an AppSync API KEY is not available or more fine-grained IAM-based access control is needed. Though, unless a flow step is essential, best practice is to keep the <em>happy path</em> as lean as possible and cut out any unnecessary steps.</p><pre>### GQL API destination ###<br>resource &quot;aws_cloudwatch_event_api_destination&quot; &quot;this&quot; {<br>  name                = &quot;gql-pub-destination&quot;<br>  description         = &quot;Send event to appSync&quot;<br>  invocation_endpoint = aws_appsync_graphql_api.this.uris[&quot;GRAPHQL&quot;]<br>  http_method         = &quot;POST&quot;<br>  connection_arn      = aws_cloudwatch_event_connection.this.arn<br>}<br><br>resource &quot;aws_cloudwatch_event_connection&quot; &quot;this&quot; {<br>  name               = &quot;gql-connection&quot;<br>  description        = &quot;A connection to appSync&quot;<br>  authorization_type = &quot;API_KEY&quot;<br><br>  auth_parameters {<br>    api_key {<br>      key   = &quot;x-api-key&quot;<br>      value = aws_appsync_api_key.this.key<br>    }<br>  }<br>}</pre><p><em>Figure 14: Adding the AppSync mutation as an API destination.</em></p><p>The final resource needed for EventBridge to directly interact with the <em>subMsg</em> mutation on the AppSync API is the EventBridge target that connects the event rule with the API destination and then constructs a HTTP POST mutation request from the information in the EventBridge event. This is not a trivial configuration and can require some tinkering to get just right.</p><p>For the example prototype, the configuration can be seen in Figure 15, where <em>data</em>, <em>channel</em>, and <em>messageId</em> are first extracted from the EventBridge event as received from the processing Lambda and then plugged back into the template of a GraphQL <em>pubMsg</em> mutation request as it looks on the HTTP layer and finally it will be invoked on the AppSync API.</p><pre>resource &quot;aws_cloudwatch_event_target&quot; &quot;this&quot; {<br>  rule           = aws_cloudwatch_event_rule.this.name<br>  target_id      = &quot;SendToAppSync&quot;<br>  event_bus_name = aws_cloudwatch_event_bus.this.name<br>  arn            = aws_cloudwatch_event_api_destination.this.arn<br>  role_arn       = aws_iam_role.eb_role.arn<br><br>  input_transformer {<br>    input_paths = {<br>      &quot;data&quot; : &quot;$.detail.data&quot;,<br>      &quot;channel&quot; : &quot;$.detail.channel&quot;,<br>      &quot;messageId&quot; : &quot;$.detail.messageId&quot;<br>    }<br><br>    # Needs to be a one-liner...<br>    input_template = &lt;&lt;EOF<br>{ &quot;query&quot;: &quot;mutation PubMsg($data:Int!,$channel:String!,$messageId:String!){ pubMsg(data:$data,channel:$channel,messageId:$messageId){ data, channel, messageId }}&quot;, &quot;operationName&quot;: &quot;PubMsg&quot;, &quot;variables&quot;: {&quot;data&quot;: &lt;data&gt;, &quot;channel&quot;:&quot;&lt;channel&gt;&quot;, &quot;messageId&quot;:&quot;&lt;messageId&gt;&quot; }}<br>EOF<br>  }<br>}</pre><p><em>Figure 15: Transforming an EventBridge event into an AppSync mutation HTTP request.</em></p><h4><em>Coming full circle</em></h4><p>This concludes the walk-through of the event flow through the architecture components with an emphasis on the end-to-end real-time event flow including the frontend by using GraphQL to support backend requests and frontend notifications asynchronously.</p><p>The Terraform configuration of certain resources was also highlighted and discussed during the walk-through, especially in situations where the configuration wasn’t obvious or non-trivial, such as configuring an SQS queue as a generic HTTP AppSync DataSource with corresponding roles and policies and configuring an AppSync mutation as a generic HTTP API destination target with authentication HTTP headers in EventBridge. Hopefully, some of these approaches and configurations can be reused or refined further by others implementing an EDA with a GraphQL frontend.</p><h3><strong>Wrap-up</strong></h3><p>Event-driven architectures and GraphQL APIs are both independently exciting emerging approaches for the development of modern, flexible, and scalable cloud applications and APIs. This article has combined the two and explored the hypothesis that we can achieve the benefits of both in a single approach.</p><p>And, indeed, the combination looks to be a promising application pattern or reusable recipe to keep handy in the cloud developer’s toolbox, despite not being fully natively supported by all the AWS services — workarounds using generic HTTP destinations have been outlined to bridge the gaps.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a588ef7b8c90" width="1" height="1" alt=""><hr><p><a href="https://medium.com/digital-mckinsey/toward-end-to-end-real-time-event-driven-architectures-using-graphql-with-aws-appsync-a588ef7b8c90">Toward end-to-end real-time event-driven architectures using GraphQL with AWS AppSync</a> was originally published in <a href="https://medium.com/digital-mckinsey">McKinsey Digital Insights</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Inclusive Design as a path to resiliency and growth]]></title>
            <link>https://medium.com/digital-mckinsey/inclusive-design-as-a-path-to-resiliency-and-growth-655193654e7f?source=rss----9c56b966f13e---4</link>
            <guid isPermaLink="false">https://medium.com/p/655193654e7f</guid>
            <category><![CDATA[government-services]]></category>
            <category><![CDATA[inclusive-design]]></category>
            <category><![CDATA[design]]></category>
            <category><![CDATA[mckinsey]]></category>
            <dc:creator><![CDATA[McKinsey Digital]]></dc:creator>
            <pubDate>Fri, 21 Jul 2023 14:52:17 GMT</pubDate>
            <atom:updated>2023-08-14T13:44:34.778Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bpp17uDzlRjDRhBZhZ0tJQ.jpeg" /></figure><p><strong>By Ruth Tupe, Expert Associate Partner, </strong><a href="https://www.mckinsey.com/capabilities/mckinsey-digital/how-we-help-clients/build-by-mckinsey/overview"><strong>Build by McKinsey</strong></a></p><p>In March, we were lucky enough to not only attend the IxDA conference but host a workshop on inclusive design, where we spoke about an opportunity we had to drive holistic impact in design in the context of government services. This workshop explored a new concept of how inclusivity is not just a social imperative, but also a strategic way to build more resiliency into products and services.</p><p>Over the past few years, the accelerated pace of technological change combined with external forces to present a set of new challenges that force us to re-evaluate our systems and in turn, how to design them. From the pandemic to population growth to the rise of AI — the world as we know it is dramatically changing. These changes offer us up the opportunity to redesign out systems with inclusivity built in; it’s our job as designers to work with these changes when we’re designing, rather than against them:</p><ul><li><strong>An aging population:</strong> As of 2020, <a href="https://www.brookings.edu/articles/what-the-2020-census-will-reveal-about-america-stagnating-growth-an-aging-population-and-youthful-diversity/#:~:text=We%20can%20expect%20all%20states,different%20for%20the%20younger%20population.">almost 50% of people are living to be 65-74 years old in the US</a>. With life expectancy higher than ever, this population offers up a massive potential workforce with a high level of professional knowledge and expertise. However, an aging population puts a strain on healthcare and the social welfare system.</li><li><strong>Growing socioeconomic divide:</strong> The COVID-19 pandemic highlighted economic inequality on a global scale. The pandemic disproportionately affected those living in poverty and while many countries were plunged into economic turmoil, the richest got richer. In fact, research carried out by <a href="https://www.oxfam.org/en/5-shocking-facts-about-extreme-global-inequality-and-how-even-it">Oxfam</a> shows that while 99% of humanity is financially worse off since the pandemic, the wealth of the world’s 10 richest men has doubled, widening the economic divide.</li><li><strong>Technological progression:</strong> <a href="https://www.ibm.com/watson/resources/ai-adoption">35%</a> of all businesses now report using AI as part of their day-to-day operations. Whilst some fear certain roles may get replaced by automation, it could drastically reduce the time employees spend on menial tasks, meaning businesses will have more capacity to do more complex work.</li><li><strong>Climate change:</strong> By now, most of us are aware of the effects of climate change but few know about the financial implications. <a href="https://climate.nasa.gov/evidence/#otp_evidence">Data shows</a> that in the last five years, 89 climate disaster events cost $595.5B in the US alone. Now we are faced with the challenge of designing infrastructure that is resistant and able to withstand more extreme weather.</li><li><strong>Hybrid working:</strong> The COVID-19 pandemic created a new normal — ‘hybrid working’. A <a href="https://www.gallup.com/workplace/390632/future-hybrid-work-key-questions-answered-data.aspx">recent study</a> showed that 38% of employees prefer this way of working and it’s not hard to see why with less commute time and better work-life balance. But it’s not without its issues; less face time with colleagues leads to a lack of connectivity in the workplace.</li></ul><p>As these shifts occur, they can often disrupt our systems and widen the inequality gap. This leads to economic, social and political polarization, with the most disadvantaged populations bearing the brunt. For example, <a href="https://www.nature.com/immersive/d41586-%20022-01647-6/index.html">research</a> shows that since the pandemic, the average income of those in the bottom 20% of earners has fallen by 6.7%.</p><p>Resiliency is a new mindset that we need to adopt. These unpredictable, novel disruptions are affecting every part of the way we live, work, and interact with the world. To deal with this we need to build resilience into every product and system we design so they can withstand shocks or surprises.</p><p>How do we become resilient to things we can’t even predict? The key is to consider inclusivity in everything we do. Traditionally, inclusivity is addressed by companies through a DEI initiative, meeting basic accessibility web requirements, or seen as a social imperative. But another way to think about inclusivity is that it puts resiliency into the fabric of solutions. When we create designs that serve a multitude of needs, for a wide array of users, that creates a more resilient design by nature. By considering a wide range of factors, use cases, and devices, you can integrate resilience into your designs from the off, rather than waiting to be reactive to a trigger.</p><p>So, how do we practice inclusive design?</p><p>First of all, it’s important to define what we actually mean. Inclusive design aims to build equitable experiences that serve the range and diversity of human backgrounds such as ability, age, culture, identity, and language persistently over time. We want to emphasize the longevity of a solution which is often overlooked. Inclusive experiences can only be possible by creating a system and tools to maintain that quality. When a new device, channel, or user group is introduced to your product or experience, are they still able to access it with the same experience parity?</p><p>This is not only important from an ethical standpoint, but from a business one too. There are a huge number of underserved communities in existence that represent a great deal of purchasing power; some experts put the figure as high as <a href="https://www.air.org/sites/default/files/2022-03/Hidden-Market-Spending-Power-of-People-with-Disabilities-April-2018.pdf">$12 trillion</a> or more.</p><p>Over the past two years we’ve developed a set of principles that enable resilient, equitable and inclusive design:</p><ul><li><strong>Broad</strong>: When designing, you must consider a wide range of users and be mindful of intersectionality.</li><li><strong>Deep</strong>: Do the work to really understand the experiences and unmet needs of potential users, including underlying systems of bias in play.</li><li><strong>Participatory</strong>: Invite users to the design process, designing “with” rather than designing “for”.</li><li><strong>Proactive</strong>: Plan for change. Anticipate unintended consequences by thinking through challenging “what-if” scenarios and prioritise building in the flexibility to adapt to changing circumstances.</li><li><strong>Measurable</strong>: Detect change. Listen for the earliest signals of harm or other unintended consequences. Build in the ability to measure and quantify from the beginning.</li><li><strong>Iterative</strong>: Enable solutions to evolve in response to changing user needs and circumstances by building a set of levers to adjust your solutions</li></ul><h4><strong>Inclusive design in government services</strong></h4><p>One area where inclusivity and resiliency matter most is in government services. As a sector it makes a great case study because it is constantly under pressure to remain operational, regardless of what else is going on in the world — and the penalty for mistakes is high. For this reason, building resilience into government systems is integral and applying inclusive design principles is the best way to achieve this.</p><p>One great example is the design of Governor’s Island in New York City. The government knew that its coasts were vulnerable to storms and hurricanes so it decided to commission landscape architect Adriaan Geuze to design a solution. Understanding the impact that climate change is having on rising water levels, Adriaan knew that it was vital to raise the shore front of the island. In 2007 he proposed building 15 ft high hills along the southernmost part of the island to protect it from storm surges and flooding. In 2012 his design was put to the test when Hurricane Sandy hit New York, causing 200,000 homes to be destroyed and more than $50 billion worth of damage. But despite the extensive damage to Manhattan, Governor’s Island was left unscathed due to Adriaan’s well-designed flood barriers.</p><p><a href="https://www.youtube.com/watch?v=ikve-HCo9hw">https://www.youtube.com/watch?v=ikve-HCo9hw</a></p><p>This project was a main source of inspiration for us, so we applied similar inclusive design principles to an unemployment services journey to see how we can build a more resilient experience for citizens. We went through a 3-step process to analyze the problem with an inclusive lens:</p><ol><li>Understanding symptoms of bias — Beyond personas, we believe greater empathy lies in taking a moment to reflect on potential prejudices and biases people may encounter. To trigger these discussions, we supplemented personas with ‘Ability’ and ‘Identity’ cards to understand intersectionality.</li><li>Generate inclusive interventions with clear ROI — We encouraged participants to not just ideate but to consider how their solution would bring business value to the organization.</li><li>Test the resiliency of your solutions — After participants created solutions, we did future scenario planning by getting participants to draw trend cards to see if their solutions would still survive (e.g., if a hurricane happened, would you solution still work)</li></ol><p>Overall, participants came up with innovative solutions to re-imagine government services that they never would have thought of before. One of the most innovative ideas that came out of the workshop was a “dating” app for pairing employees looking for work with employers. The app would allow employees to be matched quickly and efficiently with jobs that are suitable for them. The app would very much be human-centric, allowing employees and employers to ‘date’ one another on a trial basis to make sure they’re a good fit. Another idea that stuck out to us was using an AI coach to guide candidates through the job interview process, allowing them to do practice runs to build up their confidence. The AI would also have the capacity to break down language barriers throughout the job application process by coaching the applicant in their native language. The concepts and ideas generated during this workshop are a great example of how our approach to inclusive design can open the door for creativity and innovation.</p><p>Through this structured approach, we reframed inclusivity not as feel-good initiative but a strategic play with business case rationale. Too often, inclusivity initiatives are not taken seriously and deprioritized because it’s seen as a compliance activity rather than a long-term investment. It’s time to shift the conversation on what inclusivity means and utilize it properly to maximize its impact both socially and in business.</p><p>—</p><h4>Additional reading:</h4><p><a href="https://www.ibm.com/watson/resources/ai-adoption">https://www.ibm.com/watson/resources/ai-adoption</a></p><p><a href="https://www.brookings.edu/research/what-the-2020-census-will-reveal-about-america">https://www.brookings.edu/research/what-the-2020-census-will-reveal-about-america stagnating-growth-an-aging-population-and-youthful-diversity/</a></p><p><a href="https://climate.nasa.gov/evidence/#otp_evidence">https://climate.nasa.gov/evidence/#otp_evidence</a></p><p><a href="https://www.gallup.com/workplace/390632/future-hybrid-work-key-questions-answered-data.aspx">https://www.gallup.com/workplace/390632/future-hybrid-work-key-questions-answered-data.aspx</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=655193654e7f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/digital-mckinsey/inclusive-design-as-a-path-to-resiliency-and-growth-655193654e7f">Inclusive Design as a path to resiliency and growth</a> was originally published in <a href="https://medium.com/digital-mckinsey">McKinsey Digital Insights</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Effective and highly available cache: Integrating Spring Boot with Redis]]></title>
            <link>https://medium.com/digital-mckinsey/effective-and-highly-available-cache-integrating-spring-boot-with-redis-2e21c5cec8bd?source=rss----9c56b966f13e---4</link>
            <guid isPermaLink="false">https://medium.com/p/2e21c5cec8bd</guid>
            <category><![CDATA[mckinsey]]></category>
            <category><![CDATA[redis]]></category>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[spring-boot]]></category>
            <dc:creator><![CDATA[McKinsey Digital]]></dc:creator>
            <pubDate>Tue, 04 Apr 2023 14:54:31 GMT</pubDate>
            <atom:updated>2023-04-05T19:06:48.463Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JSRcZLN8Q_DZg6Pei5548A.jpeg" /></figure><p><strong>By Mohamad Assaf — Senior Analyst, </strong><a href="https://www.mckinsey.com/capabilities/mckinsey-digital/how-we-help-clients/build-by-mckinsey/overview"><strong>Build by McKinsey</strong></a><strong>; Brian Leke — Senior Expert and Associate Partner, </strong><a href="https://www.mckinsey.com/capabilities/mckinsey-digital/how-we-help-clients/build-by-mckinsey/overview"><strong>Build by McKinsey</strong></a><strong>; and Sallah Kokaina — Senior Expert and Associate Partner, </strong><a href="https://www.mckinsey.com/capabilities/mckinsey-digital/how-we-help-clients/build-by-mckinsey/overview"><strong>Build by McKinsey</strong></a></p><p>Offering fast and high-availability solutions is becoming the new norm in the field of software engineering. With the growing demands on web and mobile channels, developers can leverage open-source solutions to shape production-ready setups.</p><p>During our own practice, we have found it valuable to adopt a ‘shift-left’ approach that puts performance concerns at the core of development activities. This is especially important when it can affect end-user experience and minimize the impact of growing digital initiatives.</p><p>In this article, we will explain how implementation can be achieved on a high-availability container cluster using OpenShift to scale a Redis cache consumed by a sample microservice written with Spring Boot. Above all, we will help you avoid the pitfalls of data inconsistencies that can occur when using caching.</p><p>Using caching can stop system users from being affected by a slow response of downstream components for data that is static, but it often presents the following challenges:</p><ul><li><strong>Time-to-live </strong>—<strong> </strong>An object is cached for a specific period, usually based on the frequency of refresh or change. This requires correct configuration to ensure that data is available for the appropriate period of time before refreshing.</li><li><strong>Performance </strong>—<strong> </strong>Caching is used to speed up response times. Using the right caching practices is key to achieving the best performance.</li><li><strong>Availability </strong>—<strong> </strong>The cache may be used for operational tasks that need a quick response. Ensuring the availability of our cache will prevent any disturbance to our application.</li></ul><p>It’s important to bear these issues in mind when integrating a Redis cache with a Spring Boot application.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Fa69RZgI1KqtGo9FpTGRhg.png" /><figcaption><em>Figure 1 illustration of the integration. The backend service will call third-party APIs and use Redis to cache the response.</em></figcaption></figure><p>To kick-start a Spring Boot application, we can start by using spring initializr: <a href="https://start.spring.io/">https://start.spring.io/</a></p><h4><strong>Integration with Redis</strong></h4><p>We recommend using a Spring Boot starter to integrate with Redis, by adding the spring-boot-starter-data-redis dependency to your project’s pom.xml file. You can also add the dependency if you are using Gradle as a build tool.</p><p>Maven</p><pre>&lt;dependency&gt; <br>    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt; <br>    &lt;artifactId&gt;spring-boot-starter-data-redis&lt;/artifactId&gt; <br>&lt;/dependency&gt; </pre><p>Gradle</p><pre>implementation &#39;org.springframework.boot:spring-boot-starter-data-redis:2.2.0.RELEASE&#39; </pre><p>Specify the connection details in the application.properties file or the application.yml file using the following properties:</p><p>spring.redis.host = &lt;host_url_for_redis&gt;</p><p>spring.redis.port = &lt;port_for_redis_instance&gt;</p><p>spring.redis.password = &lt;password_for_default_redis_account&gt;</p><p>Spring will then leverage boilerplate code to establish the connection. To test the above configuration, you can use the official docker image for redis:</p><p>(docker run -p 6379:6379 -d redis:6.0 redis-server — requirepass “testPassword”)</p><p>Then add the following configuration in the properties file:</p><p>spring.redis.host = localhost</p><p>spring.redis.port = 6379</p><p>spring.redis.password = testPassword</p><p>Most articles for configuring Redis with Spring Boot suggest using the @Cacheable annotation on the main class. While this does automatically configure caching in the application, we recommend implementing caching manually, by using a caching service. This will give us the most control over all the details of our cache, such as control TTL and the structure of the Redis cache we use (Redis Strings, Redis Hashes, etc.) Below is an example of our service:</p><pre>@Component <br>@RequiredArgsConstructor <br>public class RedisService { <br>    private final RedisTemplate&lt;Object, Object&gt; redisTemplate; <br> <br>    public &lt;V&gt; void set(String cacheName, String key, V value, long timeout, TimeUnit timeUnit) { <br>        redisTemplate.opsForValue().set(cacheName+key, value); <br>        redisTemplate.expire(cacheName+key, timeout, timeUnit); <br>    } <br><br> <br><br>    public &lt;V&gt; V get(String cacheName) { <br>        return (V) redisTemplate.opsForValue().get(cacheName); <br>    } <br> <br>    public Boolean hasKey(String cacheName, String key) { <br>        return redisTemplate.hasKey(cacheName+key); <br>    } <br>} </pre><p>For performance gains over the default Java serializer, and to make it easy to inspect and debug the cache in Redis, we configure a custom serializer/deserializer. We will use a default string Serializer/Deserializer for the key, and Jackson to serialize objects. We can then configure our RedisTemplate bean using the below:</p><pre>@Slf4j <br>@Configuration <br>@Profile(&quot;!test&quot;) <br>public class RedisConfig { <br>    @Bean <br>    public RedisTemplate&lt;Object, Object&gt; redisTemplate(RedisConnectionFactory connectionFactory) { <br>        RedisTemplate&lt;Object, Object&gt; redisTemplate = new RedisTemplate&lt;&gt;(); <br>        try { <br>            redisTemplate.setKeySerializer(new StringRedisSerializer()); <br>            redisTemplate.setDefaultSerializer(new GenericJackson2JsonRedisSerializer()); <br>            redisTemplate.setConnectionFactory(connectionFactory); <br>        } catch (Exception e) { <br>            log.error(&quot;Error getting Redis Template connection &quot;, e); <br>        } <br>        return redisTemplate; <br>    } <br><br>} </pre><h4><strong>Time-to-live</strong></h4><p>Redis allows TTL configuration on the cache. If we wish to configure TTL on the entry level, in order to expire a specific object based on a refresh time, then we can use Redis strings, which are simple key-value pairs. On the other hand, if we used Redis hashes, then all fields inside the hash will be deleted when TTL expires.</p><p>Take a look at the function <strong>set</strong> in our RedisService:</p><pre>  public &lt;V&gt; void set(String cacheName, String key, V value, long timeout, TimeUnit timeUnit) { <br>        redisTemplate.opsForValue().set(cacheName+key, value); <br>        redisTemplate.expire(cacheName+key, timeout, timeUnit); <br>    }</pre><p>It takes timeout (an integer) and timeUnit (TimeUnit.Hours, or Seconds, as a unit to the timeout) as parameters. When we insert a new entry, RedisTemplate will use the cache as a key-value pair, where the key is the cache name concatenated with the key, and the value will be the serialized class using Jackson. Then, we will set the expiry duration for that cache.</p><h4>Performance</h4><p>Referring to<strong> </strong><a href="https://redis.io/docs/management/optimization/memory-optimization/">https://redis.io/docs/management/optimization/memory-optimization/</a>, we can conclude from the above that a Redis hash map with few fields will behave faster than if it had a few Redis strings. We can demonstrate this with the practical example below.</p><p>Consider case 1, a Redis hash called Object:</p><p>Object: {</p><p>1: test1,</p><p>2: test2,</p><p>3: test3</p><p>}</p><p>And case 2, where we have 3 Redis strings with keys Object1, Object2, Object3:</p><p>Object1: test1</p><p>Object2: test2</p><p>Object3: test3</p><p>Using case 1 is faster than case 2.</p><p>The reason is that Redis transforms the hash map into an array, which leverages CPU cache memory more effectively. So categorizing our entries into smaller hashes will leverage the Redis capabilities and speed up our response time. Here’s an example of how we’ve implemented this technique.</p><p>Context:</p><p>We had 30,000 customers whose information was cached in Redis. The TTL was 18 hours.</p><p>Straightforward caching strategy:</p><p>For each client, we had to insert a Redis string (key-value pairs) and cache it, leaving us with 30,000 Redis strings.</p><p>Example for a customer id = 100:</p><p>customerDetails100: {CustomerDetailsObject}</p><p>Leveraging the capabilities of Redis: <br>In this example a set of customers was mapped into a specific application user. On average, every user had 30 customers. Every time the user logged in, all customer details were fetched (from the cache). We had to structure our cache better to improve the overall performance. We achieved this using Redis hashes:</p><p>For a user id = A01, for client IDs 100 to 129</p><p>customerDetailsA01: {</p><p>100: {CustomerDetailsObject}</p><p>101: {CustomerDetailsObject}</p><p>102: {CustomerDetailsObject}</p><p>…</p><p>}</p><p>Overall, the set of 30,000 clients was divided into 1,000 sets of 30 clients each, and since all the set is filled automatically when the user logs in, we could configure TTL on the level of the Redis hash.</p><h4>Availability</h4><p>Redis may become an important system in our application for speedup and perhaps for critical operations like invalidating JWT tokens or responding to brute-force login.</p><p>In this case, when Redis goes down, it would make our application unavailable, so we have to ensure the availability of Redis. Below is a brief explanation on how we can avoid this in the two links below:</p><p>Redis provides a setup for high availability, using the Redis Sentinel <a href="https://redis.io/docs/management/sentinel/">https://redis.io/docs/management/sentinel/</a></p><p>For a way to deploy Redis to Kubernetes</p><p><a href="https://www.containiq.com/post/deploy-redis-cluster-on-kubernetes">https://www.containiq.com/post/deploy-redis-cluster-on-kubernetes</a></p><p>We used OpenShift to host our application. In this context, we can deploy Redis as a StatefulSet with two replicas. On startup, we can configure one of the instances to be a replica of the other. We can deploy Sentinel using the following:</p><pre>apiVersion: apps/v1 <br>kind: StatefulSet <br>metadata: <br>  name: sentinel <br>spec: <br>  serviceName: sentinel <br>  replicas: 1 <br>  selector: <br>    matchLabels: <br>      app: sentinel <br>  template: <br>    metadata: <br>      labels: <br>        app: sentinel <br>    spec: <br>      initContainers: <br>      - name: config <br>        image: 10.34.174.71:8082/redis:7.0.5-alpine <br>        resources:  <br>          limits: <br>              cpu: &quot;200m&quot; <br>              memory: &quot;256Mi&quot; <br>          requests: <br>              cpu: &quot;100m&quot; <br>              memory: &quot;128Mi&quot; <br>        command: [ &quot;sh&quot;, &quot;-c&quot; ] <br>        args: <br>          - | <br>            REDIS_PASSWORD=&lt;password&gt; <br>            nodes=redis-0.redis,redis-1.redis,redis-2.redis  <br>            for i in ${nodes//,/ } <br>            do <br>                echo &quot;finding master at $i&quot; <br>                ROLE=$(redis-cli --no-auth-warning --raw -h $i -a $REDIS_PASSWORD info replication | awk &#39;{print $1}&#39; | grep role: | cut -d &quot;:&quot; -f2) <br>                if [ &quot;$ROLE&quot; == &quot;master&quot; ]; then <br>                    echo &quot;found master at $i&quot; <br>                    MASTER=$i <br>                    break <br>                else <br>                    echo &quot;no master found&quot; <br>                    MASTER= <br>                    break <br>                fi <br>            done <br>            echo &quot;sentinel monitor mymaster $MASTER 6379 1&quot; &gt;&gt; /tmp/master <br>            echo &quot;port 5000 <br>            sentinel resolve-hostnames yes <br>            sentinel announce-hostnames yes <br>            $(cat /tmp/master) <br>            sentinel down-after-milliseconds mymaster 5000 <br>            sentinel failover-timeout mymaster 60000 <br>            sentinel parallel-syncs mymaster 1 <br>            sentinel auth-pass mymaster $REDIS_PASSWORD <br>            &quot; &gt; /etc/redis/sentinel.conf <br>            cat /etc/redis/sentinel.conf <br>        volumeMounts: <br>        - name: redis-config <br>          mountPath: /etc/redis/ <br>      containers: <br>      - name: sentinel <br>        image: 10.34.174.71:8082/redis:7.0.5-alpine <br>        resources: <br>          limits: <br>            cpu: &quot;200m&quot; <br>            memory: &quot;256Mi&quot; <br>          requests: <br>            cpu: &quot;100m&quot; <br>            memory: &quot;128Mi&quot; <br>        command: [&quot;redis-sentinel&quot;] <br>        args: [&quot;/etc/redis/sentinel.conf&quot;] <br>        ports: <br>        - containerPort: 5000 <br>          name: sentinel <br>        volumeMounts: <br>        - name: redis-config <br>          mountPath: /etc/redis/ <br>        - name: rdata <br>          mountPath: /data <br>      volumes: <br>      - name: redis-config <br>        emptyDir: {} <br>      imagePullSecrets: <br>        - name: regsecret           <br>  volumeClaimTemplates: <br>  - metadata: <br>      name: rdata <br>    spec: <br>      accessModes: [ &quot;ReadWriteOnce&quot; ] <br>      storageClassName: &quot;standard&quot; <br>      resources: <br>        requests: <br>          storage: 1Gi </pre><p>In the Init container, Sentinel will scan the Redis instances, find the master and monitor it. It will automatically discover the replica and will promote it to master anytime the master is unavailable.</p><h4><strong>Conclusion</strong></h4><p>Configuring effective caching can be a challenging task and when not done properly can lead to bugs, such as outdated data and reduced performance. We have provided a way to approach caching that satisfies business requirements regarding frequency of refresh, and availability to prevent system outages. We have also demonstrated how to use the intrinsic capabilities of Redis to improve performance through the structure of cached data. Follow the advice laid out in this article to speed up your implementation and application responsiveness. Please refer to the full source code and Readme on: <a href="https://github.com/mohamadassaf96/spring-redis-poc">https://github.com/mohamadassaf96/spring-redis-poc</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2e21c5cec8bd" width="1" height="1" alt=""><hr><p><a href="https://medium.com/digital-mckinsey/effective-and-highly-available-cache-integrating-spring-boot-with-redis-2e21c5cec8bd">Effective and highly available cache: Integrating Spring Boot with Redis</a> was originally published in <a href="https://medium.com/digital-mckinsey">McKinsey Digital Insights</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Integrating vulnerability scanning into your Golden Image Pipeline in GCP]]></title>
            <link>https://medium.com/digital-mckinsey/integrating-vulnerability-scanning-into-your-golden-image-pipeline-in-gcp-298198e4aab3?source=rss----9c56b966f13e---4</link>
            <guid isPermaLink="false">https://medium.com/p/298198e4aab3</guid>
            <category><![CDATA[solution-architecture]]></category>
            <category><![CDATA[vulnerability-scanning]]></category>
            <category><![CDATA[cloud-architecture]]></category>
            <category><![CDATA[gcp]]></category>
            <dc:creator><![CDATA[McKinsey Digital]]></dc:creator>
            <pubDate>Thu, 23 Feb 2023 20:13:52 GMT</pubDate>
            <atom:updated>2023-02-27T16:35:14.775Z</atom:updated>
            <content:encoded><![CDATA[<p>How to build a vulnerability scanning solution into your Golden Image Pipeline using Google Workflows and Cloud Functions</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Le__Ov9Q5IBPYLQQq1JpCQ.png" /></figure><p><strong>by Chris Do — Senior Cloud Architect, and Adrian Cortio — Senior DevOps Engineer II</strong></p><p>As you have no doubt seen, companies have already started to run critical workloads with sensitive information on public cloud platforms. Doing this requires building new types of <a href="https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/security-as-code-the-best-and-maybe-only-path-to-securing-cloud-applications-and-systems">security capabilities — ones that automate the interrogation of cloud systems against security policies</a>. This article, the fourth in a series on deploying Golden Images for GCP, dives into how to use vulnerability scanning tools to ensure systems running in GCP adhere to your company’s security policies. We will dive into how to integrate a vulnerability scanning tool that can be consumed by your Golden Image Pipeline to ensure that the images being created meet a certain security threshold. This article is not a guide or discussion on Software Composite Analysis (SCA), Static Application Security Testing (SAST), or Dynamic Application Security Testing (DAST).</p><p>Below are the other articles in this series:</p><ul><li><a href="https://medium.com/digital-mckinsey/deploy-golden-images-to-gcp-with-packer-and-cloud-build-951c36c31514">Part 1: Deploy Golden Images to GCP using Packer and Cloud Build</a></li><li><a href="https://medium.com/@mckinseydigital/update-gcp-managed-instance-groups-using-cloud-asset-feeds-d4da160119a">Part 2: Update GCP Managed Instance Groups using Cloud Asset Feeds</a></li><li><a href="https://medium.com/digital-mckinsey/use-gcp-security-command-centers-security-findings-to-track-gce-disks-that-are-using-old-golden-89f9f976f47e">Part 3: Use GCP Security Command Center’s Security Findings to track GCE disks that are using old Golden Images</a></li><li>Part 4 (this article): Integrating vulnerability scanning into your Golden Image Pipeline in GCP</li></ul><h3>What is a vulnerability scanning tool?</h3><p>A vulnerability scanning tool is used to investigate a target to look for known vulnerabilities, which are typically published as CVEs (Common Vulnerabilities and Exposures). These vulnerabilities are tied to a score that indicates the potential risk if the vulnerability were to be exploited. There are many different types of vulnerability scanning tools, but this article focuses specifically on tools that look at vulnerabilities in virtual machine images. These types of host-based scanners can look for operating system misconfigurations, outdated software patches, and sometimes network-related vulnerabilities. The vulnerability scanning tool will typically produce a report that outlines all vulnerabilities on the system and information about the vulnerability, such as the severity, date discovered, description, and remediation recommendations.</p><h3>Why should I use a vulnerability scanning tool in my Golden Image Pipeline?</h3><p>Using Packer allows us to embed some controls into the image creation process, as this is captured in code similar to infrastructure as code (IaC). However, this approach lacks the ability to inspect for vulnerabilities. Our solutions complement the use of Packer to ensure that all images generated are free of any critical or high-level vulnerabilities and consequently automatically fail image builds that don’t meet organizational security standards. By automating the approval of the image build process, we reduce the cognitive load and management overhead for security and operations teams. The vulnerability scanning tools we chose to use are Trivy (open source) and Tenable.io (commercial-off-the-shelf SaaS), but there are numerous vulnerability scanning tools that perform similarly that can be used with our architecture.</p><h3>How does the scan interact with images?</h3><p>There are various scanning methods, depending on the tool, networking, cloud provider, operating system, and access restrictions. These methods can be broken into two major categories: “agent” and “agentless.” Agent scans require a small piece of software to be installed or run on the target system. The agent is able to communicate with the management server and receive updates and scan operations. Agent scans don’t require any sort of credentials to perform privileged access scans. Agentless scans have several subcategories. We will be using a credentialed scan, and there is no software installed on the target system, making it a more lightweight solution. This means a scanner (container or VM) uses credentials to log in remotely to the target and run a set of checks. Credentialed scans require the target to have the proper ports open and our credentials to be configured as administrators so we can run elevated checks. Both approaches have their own advantages and disadvantages. We will be using both of these methods.</p><h3>How to integrate the solution into your pipeline</h3><p>There are many CI/CD tools out there that can be used to create your Golden Image Pipeline, such as Jenkins, GitLab, GitHub Actions, GCP Cloud Build, AWS CodeStar, etc., but this article will abstract the pipeline tooling and focus on the vulnerability scanning solution in GCP that will be consumed by the pipeline. There are multiple architecture solutions that can be designed to integrate a vulnerability scanning tool into a pipeline, but for our use case, we opted to use <a href="https://cloud.google.com/functions">Google Cloud Functions</a> tied together with <a href="https://cloud.google.com/workflows">Google Cloud Workflows</a>. Workflows allows you to create an event-driven architecture that connects various services together. Workflows provides us the ability to initiate a Workflows execution from a gcloud CLI command and provide a JSON payload that includes parameters to configure and initiate the vulnerability scan.</p><p>There are multiple vulnerability scanning tools available, and for this article, we opted to use a commercial-off-the-shelf (COTS) product (<a href="https://tenable.io/">Tenable.io</a>) and a free open-source product (<a href="https://www.aquasec.com/products/trivy/">Aquasec Trivy</a>) to showcase two different options for scanning images.</p><h4>Solution architecture — Tenable</h4><p>The diagram below depicts our Tenable vulnerability scanning solution from the invocation of the Google Workflows from the Golden Image Pipeline all the way to the completion of the Workflows, which returns a response back to the Golden Image Pipeline. The entire process is outlined below. Tenable.io is a COTS SaaS product that is used by many companies for their vulnerability scanning, so we felt it was a good product to demonstrate. The typical Tenable/Nessus scanning configuration involves a scanner instance that is managed by the Tenable.io console, it and will perform scans on target instances based on a scan configuration.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GEN9A2U_QGX7tJhFctZVLw.jpeg" /></figure><p><strong>1. Golden Image Pipeline</strong></p><p>The Golden Image Pipeline will create the image, as documented in the previous articles in this series. After the image is created, a new step in the pipeline includes invoking the Google Workflows to initiate the scanning process.</p><p><strong>2. Request payload</strong></p><p>In order to invoke the Google Workflows, we use a simple gcloud CLI command, which is shown below. This command is executed from the Golden Image Pipeline runner. It includes parameters that are required for the Google Workflows to execute and configure the scan, such as the recipient of the scan results, the image that needs to be scanned, the OS type, and the instance size.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IQQRYsDt3UaOzqAsXr_wrw.png" /></figure><p><strong>3. Google Workflows</strong></p><p>The Google Workflows execution begins and uses the data parameters provided to kick off a series of Cloud Functions. These Cloud Functions are serverless compute resources that run snippets of code, with each one taking input request data and returning response data. The outputs of one Cloud Function are the inputs for the next Cloud Function in the Workflows execution. The Workflows output is configured in a YAML document — an example is shown below. One of the steps includes initiating a sub-Workflow called <em>poll_workflow</em>, which acts as a polling mechanism to check on the status of the vulnerability scan, since it can take around an hour. The <em>poll_workflow</em> continues looping until the scan is complete.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/759/1*t5Ne1uYugQW7J3eZMuxX1Q.png" /></figure><p><strong>4. Google Cloud Functions</strong></p><p>There are many Cloud Functions that are used to complete the Workflows, which are detailed below:</p><ul><li><em>create_target_vm</em>: This function receives the Workflows execution payload, including the image, type, operating system, and contact email. It then creates a target VM using the input parameters that will be scanned by the Nessus Scanner.</li><li><em>initiate_scan</em>: This function receives the response from the <em>create_target_vm </em>function, including the instance name, instance IP address, operating system, and contact email. These values are then used to initiate a connection to the Tenable.io API, which then starts the scan using our Nessus Scanner VM that is located in the same subnet as the newly created target VM. This generates a scan_id, which is used to check the status of the scan and retrieve the report when it’s completed.</li><li><em>poll_function</em>: This function receives the response of the <em>initiate_scan</em> function, which includes the scan_id. This function then initiates a Tenable.io API call to check the status on that scan_id. The function then returns the status of “complete” or “running” back to the Google Workflows, which will loop back into this function after a short wait period if the status is “running.”</li><li><em>handle_results</em>: This function receives the response of the <em>poll_workflow</em> sub-Workflow, which includes the scan_id. This function then initiates a Tenable.io API call to retrieve the report on that scan_id. The report is stored in a Google Cloud Storage Bucket in CSV format. This function will also delete the target VM and remove the asset from Tenable.io for licensing purposes. Finally, this function returns the number of vulnerabilities for each severity, as well as the URL to download the CSV from the Storage Bucket.</li></ul><p><strong>5. Other resources</strong></p><p>There are some supporting resources used in this solution, which are described below:</p><ul><li><em>Nessus Scanner VM</em>: This is a persistent compute engine instance that is used to scan all of the target instances that contain the image that needs to be scanned. The Nessus Scanner VM communicates with Tenable.io to initiate scans and send results.</li><li><em>Google Cloud Storage Bucket</em>: This bucket is used to store artifacts and scan results. The artifacts are things such as the Cloud Functions build files, startup scripts, scan results, etc.</li><li><em>Google Cloud Networking</em>: These are the networking resources required to have the solution running on GCP, such as a virtual network, subnet, firewall rules, Cloud NAT, etc.</li><li><em>Tenable.io</em>: This is the vendor’s console/management interface that controls the Nessus Scanner VM and also contains all of the data and metrics related to the scans.</li></ul><p><strong>6. Response payload</strong></p><p>The response payload is returned by the Google Workflows to the caller, which in this case is the Golden Image Pipeline. The response contains the number of vulnerabilities by severity as well as the URL to download the scan report from Cloud Storage. An example response payload is shown below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zaukE7NSJCb2N5_nfvA0gg.png" /></figure><p><strong>7. Sample Tenable.io report</strong></p><p>Below is an example scan report of the image we used from the Tenable.io web interface:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*byeV1doBpO09Y4mFPP58FA.png" /></figure><h4>Solution architecture — Trivy</h4><p>The diagram below depicts the architecture for the Trivy vulnerability scanning solution from the invocation of the Google Workflows from the Golden Image Pipeline all the way to the completion of the Workflows, which returns a response back to the Golden Image Pipeline. Trivy is an open-source scanning solution that is quick and easy to get started. One limitation of Trivy is that it only scans Linux instances natively. As you can see, this solution uses fewer Cloud Functions. This is due to the nature of how Trivy works — it’s a simpler architecture. Trivy is downloaded and installed on the target instances, and a simple CLI command is executed on the target instance to execute the scan. The scan output is then saved to Cloud Storage. The entire process is outlined below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ju-oSOytUS8PxD38uQgpZg.png" /></figure><p><strong>1. Golden Image Pipeline</strong></p><p>The Golden Image Pipeline will create the image, as documented in the previous articles in this series. After the image is created, a new step in the pipeline includes invoking the Google Workflows to initiate the scanning process.</p><p><strong>2. Request payload</strong></p><p>In order to invoke the Google Workflows, we use a simple gcloud CLI command, which is shown below. This command is executed from the Golden Image Pipeline runner. It includes parameters that are required for the Google Workflows to execute and configure the scan, such as the recipient of the scan results, the image that needs to be scanned, the OS type, and the instance size.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0qF1-SllLv2Bu1bvlkXUeQ.png" /></figure><p><strong>3. Google Workflows</strong></p><p>The Google Workflows execution begins and uses the data parameters provided to kick off a series of Cloud Functions. These Cloud Functions are serverless compute resources that run snippets of code, with each one taking input request data and returning response data. The outputs of one Cloud Function are the inputs for the next Cloud Function in the Workflows execution. The Workflows output is configured in a YAML document – an example is shown below. One of the steps includes initiating a sub-Workflow called <em>poll_workflow</em>, which acts as a polling mechanism to check on the status of the vulnerability scan, since it can take around an hour. The <em>poll_workflow</em> continues looping until the scan is complete.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/849/1*sTi9gSiE7x5erLUIkKSC8g.png" /></figure><p><strong>4. Google Cloud Functions</strong></p><p>There are many Cloud Functions that are used to complete the Workflows, which are detailed below:</p><ul><li><em>create_target_vm</em>: This function receives the Workflows execution payload, including the image, type, and operating system. It then creates a target VM using the input parameters that will be scanned. This instance is created using a startup script, which is detailed below:</li></ul><p><strong><em>startup</em>.<em>sh</em></strong></p><ol><li><em>Install jq and Trivy</em>: These are packages required for parsing and running the scan.</li><li><em>Run Trivy scan</em>: The Trivy package is initiated with a simple CLI command:</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/443/1*e46owcX7Ikm3Wo0I4WMoig.png" /></figure><p>3.<em> Retrieve metadata</em>: Metadata is retrieved from the instance to access details about where to upload scans.</p><p>4.<em> Parse and upload</em>: The scan results are parsed using jq to find the count of each severity, and then the JSON file is uploaded to Cloud Storage.</p><p>5.<em> Delete VM</em>: Finally, the VM deletes itself using the Google Cloud REST APIs for Compute Engine.</p><ul><li><em>json2csv</em>: This is a simple function that triggers on Cloud Storage Bucket events in the specific Cloud Storage Bucket/Folder and will convert the JSON to CSV. This was added because the jq to CSV feature wasn’t working properly with the dataset provided by Trivy, so we created a Cloud Function to handle this more elegantly. This function also adds unique metadata to the CSV object so that the solution can check for new CSV files that match the Google Workflows execution.</li><li><em>check_scan_status</em>: This function checks the CSV folder in the Cloud Storage Bucket for new CSV files that have metadata matching the Google Workflows execution. If there is no match, the Workflows will wait a short period and retry this function again. Once the matching CSV file is found, the scan is complete, and the function returns the vulnerability data and the CSV URL to download the file.</li></ul><p><strong>5. Other resources</strong></p><p>There are some supporting resources used in this solution, which are described below:</p><ul><li><em>Google Cloud Storage Bucket</em>: This bucket is used to store artifacts and scan results. The artifacts are things such as the Cloud Functions build files, startup scripts, scan results, etc.</li><li><em>Google Cloud Networking</em>: These are the networking resources required to have the solution running on GCP, such as a virtual network, subnet, firewall rules, Cloud NAT, etc.</li></ul><p><strong>6. Response payload</strong></p><p>The response payload is returned by the Google Workflows to the caller, which in this case is the Golden Image Pipeline. The response contains the number of vulnerabilities by severity as well as the URL to download the scan report from Cloud Storage. An example response payload is shown below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hsnU4crH-DbgnAnJ81Rucw.png" /></figure><p><strong>7. Sample Trivy CSV report</strong></p><p>Below is a snippet of the Trivy CSV scan report:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dShXJqhBGOkxWQXdgut3Sw.png" /></figure><h4>Conclusion</h4><p>In this article, we showed a way to integrate two different vulnerability scanning solutions into your Golden Image Pipeline using GCP Workflows and GCP Cloud Functions. We showed two different approaches to image vulnerability scanning, one being an open-source solution (Trivy), and the other being a commercial-off-the-shelf solution (Tenable.io). Either of these solutions can accommodate most common use cases. These vulnerability scans can be a useful step in your image process to fail builds that meet a certain threshold of vulnerabilities. This is a great way to automate the vulnerability management process for images within your environment. You can then limit your instance deployments to only allow the use of approved Golden Images.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=298198e4aab3" width="1" height="1" alt=""><hr><p><a href="https://medium.com/digital-mckinsey/integrating-vulnerability-scanning-into-your-golden-image-pipeline-in-gcp-298198e4aab3">Integrating vulnerability scanning into your Golden Image Pipeline in GCP</a> was originally published in <a href="https://medium.com/digital-mckinsey">McKinsey Digital Insights</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Overheard at QCon 2022: Navigate complex environment and evolving relationships]]></title>
            <link>https://medium.com/digital-mckinsey/overheard-at-qcon-2022-navigate-complex-environment-and-evolving-relationships-9a025034c1dd?source=rss----9c56b966f13e---4</link>
            <guid isPermaLink="false">https://medium.com/p/9a025034c1dd</guid>
            <category><![CDATA[mckinsey]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[team-building]]></category>
            <category><![CDATA[networking]]></category>
            <category><![CDATA[qcon]]></category>
            <dc:creator><![CDATA[McKinsey Digital]]></dc:creator>
            <pubDate>Mon, 23 Jan 2023 20:08:17 GMT</pubDate>
            <atom:updated>2023-04-05T19:08:43.363Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/612/1*-96jUkUBBWEft66JWf6kVw.png" /></figure><p><strong>By Katterine Rodriguez and Victor Souza — Specialists, </strong><a href="https://www.mckinsey.com/capabilities/mckinsey-digital/how-we-help-clients/build-by-mckinsey/overview"><strong>Build by McKinsey</strong></a></p><p>We recently had the pleasure of participating in QCon, a global conference that gathers the best engineers from top-notch innovation companies. The event covers a wide range of relevant software engineering and leadership topics related to the kind of projects that have been carried out in our region.</p><p>We have learned a lot about how we can drive innovation and create meaningful impact for our clients by networking with people that face similar challenges and are implementing the best practices in technical projects. One of the most meaningful sessions was “<em>Navigate complex environments and evolving relationships</em><strong>” </strong>by Jennifer Davis, Engineering Manager at Google, who shared valuable information about how to build a team and align it to deliver business value.</p><h4>Navigating complex environments and evolving relationships</h4><p>It’s no secret that companies expect staff to collaborate effectively, communicate well, and focus on their missions from day one. However, in the real world, such things do not always happen without the intentional effort to create an environment that fosters<strong> autonomy, trust, diversity of opinions and recurrent learning &amp; improvement</strong>. The first step of this intentional effort is the acknowledgment that all team members must learn and practice how to collaborate effectively. This is known as team development.</p><p>The researcher and psychologist Bruce Tuckman developed a model that described the stages through which a group of people turn into a high performing team. His model has five stages — <strong>forming</strong>, <strong>storming</strong>, <strong>norming</strong> <strong>performing</strong>, and <strong>adjourning </strong>— and it is used as a reference by agilists.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/650/1*Y6cyxkgMvSbfD3FmiiZThg.png" /></figure><ul><li><strong>Forming stage: </strong>This happens when the team first comes together. Most members are kind and motivated, but some of them might be anxious and they look to a group leader for direction and guidance on how to tackle problems.</li><li><strong>Storming stage</strong>: This begins to occur while planning and executing tasks. At this stage, team members express their own points of view and showcase their personal work styles. In addition to that, it may raise structural issues, power struggles, confusion, and unclear role definitions.</li><li><strong>Norming stage:</strong> At this point, team members start to create stronger relationships, cohesion, demonstrate a sense of trust, and resolve their differences. The teamwork achieves better capacity to solve problems and at the same time, the leader starts to share responsibility with the group.</li><li><strong>Performing stage:</strong> When teammates focus and share the same mission and vision, exchange feedback with project managers and offer positive support to each other, team leaders can delegate more tasks and give autonomy to their team players.</li><li><strong>Adjourning stage</strong>: This happens once the job is done and it’s time for team members to leave the project. There are often feelings of sadness and reflection. It’s the perfect opportunity to recognize team efforts and achievements.</li></ul><p>Understanding Tuckman’s group development process can help you enhance the performance of your teams. The question is, how can you help your team to go through those stages in the most effective way? According to Jennifer there are three key areas that drive team development:</p><h3><strong>1. Functional Leadership</strong></h3><p>“Leadership is not defined by the exercise of power but by the capacity to increase the sense of power among those led.<strong> </strong>The most essential work of the leader is to create more leaders<strong>.” </strong>— Mary Parker Follett, The Creative Experience, 1924</p><h4><strong>Awareness &amp; Trust</strong></h4><p>It’s crucial that people feel respected for who they are, what they know and what they do. You should therefore get to know your team by learning about everyone’s motivations, objectives &amp; concerns in order to recognize the strengths and weaknesses of each team member.</p><p>Another element is ensuring you share your own core values, experiences, and vulnerabilities. This will allow the team to get to know you better and you can start building trust with one another.</p><h4><strong>Open communication</strong></h4><p>Several management tools help us to organize and formalize tasks, but that is not enough to break the ice among teammates. A lack of open communication increases the likelihood of difficult tasks failing due to team members not sharing estimations, details or scenarios and avoiding suggesting improvements or reporting risks on time.</p><p>To facilitate open communication, offer teammates face-to-face virtual or physical spaces and increase probabilities for unplanned interactions among them. In addition, provide spaces where the team and business stakeholders can share a common vision of the final product.</p><h4><strong>Team Norms</strong></h4><p>Team Norms are a basic set of agreements defined by the members of the team about how to work well together. The teammates should discuss common practices and processes and define agreed upon behaviors.</p><p>For example, the team norms could include:</p><ul><li>Working inside scheduled sessions</li><li>Establishing clear goals for each work session</li><li>Focusing on resolving problems without placing blame</li><li>Raising the alarm if there is a potential risk</li><li>Keeping an open mind for colleague suggestions</li><li>Maximizing the reuse of previous workstreams</li></ul><h4><strong>Delegate leadership</strong></h4><p>Functional leadership lies in the assumption that all individuals are capable of leading. For the team to scale, leadership should be developed to suit the pace of each team member while making the team leader’s intentions clear so that the people have the right opportunity and support required to learn over time.</p><p>This doesn’t mean that clearly defined roles and responsibilities aren’t in place. Instead, this allows all team members to hold different leadership roles and fosters ownership of different parts of the delivery. It also facilitates knowledge sharing, workload and even work life balance to be spread more fairly across the team.</p><h3><strong>2. Boundary setting</strong></h3><h4><strong>Team rituals</strong></h4><p>In all dimensions of life, people have rituals, such as enjoying a cup of coffee to start the day with your coworkers or eating pizza each Friday to spend time with your family.</p><p>A ritual can create a strong, long-lasting personal connection so it’s a powerful tool to boost your team’s performance. If you promote some rituals in daily routines, you can help to promote an overarching team identity. A practical example is sharing a common playlist which all team members feed into that marks the start of each work session. Similarly, you could use an online wheel in order to randomly assign the order in which each team member participates during the daily ceremony session.</p><p>And at McKinsey, we also have wonderful team rituals. In our projects, it’s common to take a five-minute personal KPI (Key Performance Indicator) session before a daily check-in to keep track of everyone’s personal development points like “do exercises three times a week” or “only drink zero sugar soft drinks”. Another good example is to schedule a dinner from time to time to reinforce connections between the team members. And with that, everyone can motivate their colleagues to accomplish their personal KPIs and create strong relationships.</p><p>It’s important to select and define the team rituals with everyone so they can suggest what they want to include, engage, and enjoy each session. This isn’t something that should be dictated by the team leader, instead it should become part of the team’s ways of working.</p><h4><strong>Set and uphold boundaries</strong></h4><p>Setting boundaries allows us to get control over our family, friendships, projects, work life and how we effectively split our time and effort. If we are talking about our work, it’s important to establish boundaries ahead of team collaboration.</p><p>Team members should agree on the boundaries they need to adhere to so everyone feels comfortable working together. So, identify roles and responsibilities for each member of the team, the amount of time they should spend at work, their main priorities, when and how they communicate with each other, what the official channels of communications are, and determine capacity and speed of each member of the team. Offer alternatives to find win-win solutions and maintain a friendly work environment while supporting each other’s boundaries.</p><p>Here’s an example Jennifer shared on how she documents boundaries and set expectations with her team:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*Ms1qdYg3AjqZc5ZvRQwCOg.png" /></figure><h3><strong>3. Learning and Adaptability</strong></h3><h4><strong>Individual feedback sessions (1:1)</strong></h4><p>This is a session between two members of the team where they both can look back on previous stages of the project, to evaluate and share their thoughts about the progress made. These feedback sessions allow people to reflect, encourage, recognize, learn, and seek ways to help their peers in their personal and professional growth. It’s also vitally important that these sessions are free of judgement so team members can express opinions openly, to strengthen communication, trust and remove the fear of making mistakes.</p><h4><strong>Learning opportunities</strong></h4><p>Change is constant, so it’s important to prepare you and your team to adapt and overcome challenges as they emerge. Jennifer believes that each team should dedicate time to training and learning during their planning sessions.</p><p>Discuss this with your team as well as team leaders. Your team’s ability to tackle problems depends on their knowledge and motivation, so having both training and learning sessions is essential to foster innovation, create business differentiators and should be seen as an investment.</p><p>Attending conferences can be a great way to keep your team updated on current tech trends and emerging technologies but also taking the time to attend technical leadership training can help develop your team members’ skills.</p><h4><strong>Implement Decision Records</strong></h4><p>This is a technique that has been increasingly adopted over the last few years, whereby using simple text-based documents describing each technical decision allowed users and other engineers to understand the rationale behind any decision that shaped the product or software.</p><p>These documents should consider things like:</p><ul><li>Purpose, definition, and consequences of this technical decision</li><li>Urgency of this decision</li><li>Who is making this decision — an individual or a group?</li><li>How is this decision going to be enforced?</li><li>Who needs to be made aware of and align with this decision?</li></ul><p>Jennifer also shared a link to her team’s decisions records here (<a href="https://bit.ly/emblem-decisions">https://bit.ly/emblem-decisions</a>) and we’d like to also share with you the Architecture Decisions Record (ADR) GitHub repository (<a href="https://adr.github.io/">https://adr.github.io/</a>) where you can find more details about this technique and find other examples and templates to use with your team.</p><p>To sum up, Jennifer explained how difficult it is to navigate complex environments and evolving relationships in development teams, while also giving us a set of tools to build strong team relationships through open communication, and incredible teamwork.</p><p>We believe that being able to foster a trustful, knowledgeable, and balanced environment is essential for a healthy work environment. It is vital to identify what stage our team is at, and the key areas that drive its development to propose appropriate strategies that support rapid evolution to ensure high performance. Remember that a team’s culture and ability to operate properly takes time. Include people at all levels when making an action plan to help create healthy and efficient teams.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9a025034c1dd" width="1" height="1" alt=""><hr><p><a href="https://medium.com/digital-mckinsey/overheard-at-qcon-2022-navigate-complex-environment-and-evolving-relationships-9a025034c1dd">Overheard at QCon 2022: Navigate complex environment and evolving relationships</a> was originally published in <a href="https://medium.com/digital-mckinsey">McKinsey Digital Insights</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Overheard at QCon: Designing and delivering effective microservices]]></title>
            <link>https://medium.com/digital-mckinsey/overheard-at-qcon-designing-and-delivering-effective-microservices-cf476b6976cc?source=rss----9c56b966f13e---4</link>
            <guid isPermaLink="false">https://medium.com/p/cf476b6976cc</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[mckinsey]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[microservices]]></category>
            <dc:creator><![CDATA[McKinsey Digital]]></dc:creator>
            <pubDate>Wed, 07 Dec 2022 14:33:19 GMT</pubDate>
            <atom:updated>2022-12-07T14:35:32.704Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dz34SIdf47wDUvOcEF9UeA.jpeg" /></figure><p><strong>By: Tassio Abreu — Digital Specialist, McKinsey &amp; Company</strong></p><p>Anyone that has ever attended QCon will no doubt agree that it truly is one of the highlights of the year for software engineers and 2022 was no different. Microservices have always been an area of interest for me and one our clients regularly want to discuss with us. I made sure Microservices was a key focus of my time at QCon.</p><p>As you’d have guessed from the title, this article shares my key takeaways from the conference on how I recommend to design and deliver effective microservices. However, before doing so, it’s important to understand the benefits of microservices and how to secure the most value from the approach.</p><h4><strong>Starting your microservices journey</strong></h4><p>Everything starts with design. When new teams or new products are being conceived, many jump straight to microservices as this style of architecture is very much on trend currently. It’s important to differentiate between architectures you’re interested in and those that you really need, though.</p><p>During his QCon session, <a href="https://plus.qconferences.com/speakers/chrisrichardson">Chris Richardson</a>, software architect and serial entrepreneur who founded Microservices.io, used metaphorical dark energy and dark matter to compose a memorable framework to help with exactly this, focused on repulsion forces and attraction forces.</p><p>He defined the repulsion forces, in this context, as:</p><ul><li><strong>Simple components:</strong> when services are segregated, it’s much easier to analyze a codebase from scratch and understand its entirety</li><li><strong>Increase team autonomy:</strong> teams can focus on small pieces or business functions and thus be more responsible for its end-to-end success, from implementation to customer impact</li><li><strong>Fast deployment pipeline:</strong> smaller systems are deployed faster, the lead time for DORA metrics is often faster and from code to deployment benchmarks tend to be closer to elite levels</li><li><strong>Support multiple technology stacks:</strong> you can always use the best technology at hand, whether to increase performance, to use a framework that is more advanced and can lead to improved ergonomics and engineering time, or for compliance purposes</li><li><strong>Cost-effective scaling:</strong> you can scale pieces of your application separately, allowing for a new concept of scaling through container density, which in turn diminishes the chances of having to overprovision up to known peak capacity to avoid problems in uptime</li><li><strong>Segregate highly available components:</strong> you can allocate different services in different infrastructures, and this can cause variations in both cost and availability</li></ul><p>On the other hand, he defined attraction forces as:</p><ul><li><strong>Simple interactions:</strong> importing a class or file and using it directly will always be simpler, cheaper and fast than having to connect through a network with another component</li><li><strong>Prefer ACID over BASE:</strong> avoiding eventual consistency allows for better and an always-true system behavior — there’s no risk that information being hot-read is inconsistent</li><li><strong>Minimize runtime coupling:</strong> service availability will depend less on other moving pieces. The more distributed a system is, the more expensive it is to implement resilience, as more moving parts often result in increased complexity and a higher risk of failure</li><li><strong>Efficient inter-service communication:</strong> communication often happens in-memory, ensuring it’s fast and predictable</li><li><strong>Minimize design time coupling:</strong> your service will need to change less frequently, especially in response to external forces. Imagine having to redeploy your system regularly due to a dependence on an external REST API of a service that keeps changing. It’s far from the ideal approach</li></ul><p>As a rule of thumb, it’s recommended to begin with attraction forces, as keeping them together is often easier and provides engineering teams with a stronger platform for eventual success. It’s also important to keep in mind that you don’t need a distributed team at this stage, unless it’s project critical.</p><p>After starting small, keep challenging your architecture with real, proven hypotheses for repulsion. It’s here that distributed systems begin to offer value, as although they’re more complex to maintain, they have better end-game potential for scalability and resilience. This does come at a price, but Chris defined and shared <a href="https://microservices.io/patterns/">multiple patterns</a> to help here.</p><p>It’s important to understand that system design is an iterative process, so constantly reevaluate how to evolve your system. It can even prove beneficial to decompose it into multiple sections in terms of decoupling, scalability, enhanced team topology and enabling two-speed IT. The latter is a <a href="https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/a-two-speed-it-architecture-for-the-digital-enterprise">framework by McKinsey</a> which showcases how teams that require stability while delivering disruptive, innovative solutions.</p><p>So how should you iterate on your system design? The following is derived from Chris’ pattern language:</p><ul><li>Define the context of your system, e.g: an eCommerce system being evolved to increase conversation rates</li><li>Enumerate the problems being tackled, e.g: there’s a big churn before checkout due to lengthy user log-in times</li><li>Note the forces that help prioritize each problem, e.g: new team members must quickly become productive as the checkout service is critical and new deployments can’t happen regularly due to the risk associated</li><li>Design the solution, e.g: the checkout functionality will be broken down into a new microservice, to decouple its lifecycle from other components that can and should change more often to adapt to users’ behavior</li><li>Implementation details, code commits and effective code changes that bring your new system to life</li><li>Resulting content, e.g,: C4 diagrams, new ADRs and other content to illustrate how the system will behave and interact</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*rll9kE9CqDD9MbPk" /></figure><h4><strong>Taking care of overcomplicated architecture early</strong></h4><p>At the second session I attended at QCon, <a href="https://plus.qconferences.com/speakers/cassandrashum">Cassandra Shum</a>, enterprise architect and former member of the Technical Advisory Board at Thoughtworks, discussed the importance of avoiding overcomplicated architecture which perfectly complemented Chris’ session. She strongly believes that driving a culture of engineering excellence takes care of net-zeroing tech debt early in any project, and I have to agree. Especially having now witnessed her framework which follows-up on the growing complexity of architecture based on symptoms vs remedies.</p><p>The following are the areas suggested engineers focus on to diagnose overcomplicated architectures:</p><ul><li><strong>Distributed monoliths:</strong> this is the worst possible scenario. Teams try to dive into microservices, but end up with replicated code, dysfunctional patterns and outputs which don’t reap any benefits of either architecture</li><li><strong>Tracing, logging, monitoring and visibility without benefit:</strong> nobody knows what’s going on and logs become an expensive clone of the famous green screen in The Matrix which rarely leads to problem solving</li><li><strong>Too much abstraction:</strong> indirection can lead to delays and unnecessary moving parts that bring additional risk and needless complexity</li><li><strong>High cognitive load:</strong> the system is so complex to understand that onboarding new developers, irrespective of how good they are, is always a burden. Changing a single line of code has so many potential side effects that teams simply prefer to stagnate</li><li><strong>Difficulty making technical decisions:</strong> too many cooks spoil the broth, and this proves it. When too many people need to be brought into discussions prior to making changes, teams can’t evolve their business functions</li><li><strong>A ‘build in-house’ mindset: </strong>The perfect example is a fintech implementing its own authentication system instead of focusing on innovation that will set them apart from the competition. Every time a business builds something in-house which could have been bought “off-the-shelf”, that’s one less team potentially steering the company forward</li></ul><p>Remedies on the other hand do exactly what they say on the tin, providing solutions to the aforementioned symptoms:</p><ul><li><strong>Have clear domain boundaries: </strong>align your business with your code. This can be achieved by having a good, yet lightweight, understanding of domain driven design, as well as spending time defining an ubiquitous language that is spoken from directors down to the engineers seamlessly</li><li><strong>Observability and monitoring: </strong>find a process which allows real-time inspection of the inner workings of your system, from the outside. Don’t ignore anomalies, be alarmed by them. Ensure people around you understand the system and the importance of cooperating with product teams on debugging</li><li><strong>Lightweight architecture decision records (ADRs): </strong>There shouldn’t be excessive or unknown side effects, so changing the system shouldn’t be a daunting task</li><li><strong>Cross-functional requirements: </strong>leverage patterns like microservices chassis to reduce the time teams spend work on activity which could be done just the once before being centralized and reused. The likes of sidecars, proprietary libraries and packages really help at this point</li><li><strong>Remove the hero culture: </strong>if critical knowledge is owned by one person, you have a very low <a href="https://www.agileadvice.com/2005/05/15/agilemanagement/truck-factor/">truck-factor</a> as if that person is unavailable or leaves, a project can come to a quick halt</li><li><strong>Controlled technical debt: </strong>your team should know how to measure technical debt, be aware of issues with code quality and react to such issues to ensure best practice</li><li><strong>Developer effectiveness: </strong>codes can be changed by a new engineer without risk as it’s protected by automation and has controlled and known side effects</li><li><strong>Platform thinking: </strong>product teams are incentivized to contribute, donate and eventually platformize team-specific tech assets to the company as a whole</li><li><strong>Commodity vs differentiation: </strong>teams should spend as much time as possible working on new features and potential revenue streams or end-user perceived quality, instead of unnecessary heavy lifting against the system’s own weight and inertia</li></ul><h4><strong>Effectively operating your distributed system</strong></h4><p>With your microservices journey now well under way and a clear framework in mind for tackling overly complex architecture, attention should turn to effectively operating your distributed system. During his talk at QCon, <a href="https://qconsf.com/speakers/wandereleisouza">Wanderlei Souza</a>– a distinguished engineer from Build by McKinsey — put together a collection of best practices that feels like the missing piece of the designing and delivering microservices puzzle.</p><p>However, before introducing a distributed system, there are three crucial steps that should be taken:</p><ul><li><strong>Have design patterns as a handy problem-solving resource:</strong> common solutions to common problems are your best bet to keep your system manageable and evolvable over time. Make sure engineers are fluent on a pattern language for distributed architectures</li><li><strong>Create a culture of parsimony:</strong> focus on simple solutions. Use a framework capable of taming overengineered architectures to avoid unnecessary — and usually accidental — complexity</li><li><strong>Implement observability and alarming:</strong> you can use a platform that better suits your team preferences, needs and budgets</li></ul><p>Once these steps have been taken, it’s vital that engineering teams work together with the wider business stakeholders to align on objectives and outcomes. With that in mind, the following considerations will ensure your distributed system is infinitely more effective:</p><ul><li><strong>Don’t forget about the humans:</strong> make sure that engineering teams and product / business focused colleagues are aligned. Avoid the scenarios in which a new Black Friday push notification campaign can generate a traffic surge that damages or even causes service downtime.</li><li><strong>Master patterns and trade-offs for service communication: </strong>favor orchestration for complex workflows, choreography for scalability and weaker coupling</li><li><strong>Design for resilience: </strong>consider concepts such as:</li><li>Graceful degradation</li><li>Fault tolerance</li><li>Circuit breakers</li><li>Bulkhead</li><li>Business-aware health checking</li><li>Distributed tracing for end-to-end debugging</li><li>Log aggregation</li><li>Anomalies on application performance metrics</li><li><strong>Disliking TDD but resisting letting go of high testability:</strong> make sure you can easily simulate real-world situations at test environments, record the operations of a given day and simulate or replay these deliberately. It’s also key that you’re notified of changes on external integration contracts and that you’re able to reproduce composed scenarios when lag or system failure occurs</li><li><strong>Measuring your success:</strong> it’s vital you can evaluate the overall quality of your system against a well-respected industry standard, such as <a href="https://iso25000.com/index.php/en/iso-25000-standards/iso-25010">ISO 25010</a></li></ul><h4><strong>Microservices aren’t a silver bullet</strong></h4><p>Operating microservices isn’t a walk in the park and certainly isn’t the silver bullet for achieving engineering excellence that many paint it as. Despite the acknowledged complexity, we all accept that being aware and understanding these concepts can help any kind of software engineer or architect in any sector — whether that’s a cloud-native, written-from-scratch startup, or an incumbent company that needs to be brought into the 21st century.</p><p>Eight years after Chris’ ground-breaking first talk about the microservices architecture, tools and patterns, community knowledge on the topic has grown exponentially. It’s interesting to see how widespread and well-adopted the architecture is and how its applicability suits the need of modern businesses in the era of agile, team topologies and subscription services. Especially as, when managed well, microservices can be a lever for increased engineering agility.</p><p>So while it’s a complex area of focus, if your team relies on patterns with a proven track record, allows for observability and takes the time to implement controllability early in the microservices journey, success will be within touching distance.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cf476b6976cc" width="1" height="1" alt=""><hr><p><a href="https://medium.com/digital-mckinsey/overheard-at-qcon-designing-and-delivering-effective-microservices-cf476b6976cc">Overheard at QCon: Designing and delivering effective microservices</a> was originally published in <a href="https://medium.com/digital-mckinsey">McKinsey Digital Insights</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Overheard at Qcon: Accelerating teams and continuous deployment]]></title>
            <link>https://medium.com/digital-mckinsey/overheard-at-qcon-accelerating-teams-and-continuous-deployment-2be5bf75f75e?source=rss----9c56b966f13e---4</link>
            <guid isPermaLink="false">https://medium.com/p/2be5bf75f75e</guid>
            <category><![CDATA[mckinsey]]></category>
            <category><![CDATA[qcon]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[software-engineer]]></category>
            <dc:creator><![CDATA[McKinsey Digital]]></dc:creator>
            <pubDate>Mon, 21 Nov 2022 15:46:58 GMT</pubDate>
            <atom:updated>2022-11-21T15:46:58.266Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>by Rishi Markenday — Expert, McKinsey &amp; Company</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*t28ck7t-eRNsY5x8oBUsUA.jpeg" /></figure><p>For those who have never experienced the wonders of QCon, it’s one of the leading events in the software engineering calendar, focusing on the latest software trends and cutting-edge innovations. Taking place in San Francisco late last month, I was fortunate enough to attend and witness first-hand discussions on the future of the software development industry and certainly had my eyes opened by some of the talks I attended.</p><p>As the title of this article suggests, I’ll be focusing on two areas of inspiration following a couple of insightful sessions I attended during my time at the conference.</p><h4><strong>Accelerating teams and scaling-up engineers</strong></h4><p>One such session I attended was hosted by Argentinian eCommerce business Mercado Libre which describes itself as a “marriage between Amazon and PayPal.” The discussion focused on scaling-up engineering teams, with the key motto hammered home being ‘accelerating teams, controlling organizational entropy.’</p><p>To say the business has been on quite a journey since launching its architecture as a monolith in 2000 would be an understatement given that today it has the Fury platform to enhance developer experience and have been scaling the number of engineering teams since 2020. In fact, the average day sees Mercado Libre responsible for 10,000 deploys, 26,000 microservices and training 700 machine learning models.</p><p>This journey was a key discussion point during the session, outlining that 83% of Mercado Libre’s developers have less than two years with the organization. When it had a more modest team, it could get away with providing a single career path which saw seniority aligned directly with managerial responsibilities. However, as Mercado increased its headcount, it also created a parallel career path where employees could grow their technical skillset without managing other developers. This is built on the premise that experts lead experts, similar to the approach Harvard Business Review suggests is a primary reason for Apple’s success.</p><p>The team structures saw 85% of developers positioned within delivery teams responsible for building customer-facing products, while the remaining 15% were part of platform teams. These deliver teams are cross-functional and consist of:</p><ul><li>Front-end developers</li><li>Back-end developers</li><li>Machine learning engineers</li><li>Native developers</li><li>Data engineers</li><li>User experience experts</li></ul><p>Simultaneously, Mercado Libre’s platform teams are split by:</p><ul><li>Cloud engineers</li><li>Security engineers</li><li>Machine learning engineers</li><li>Quality engineers</li><li>Data analytics engineers</li><li>Site reliability engineers</li></ul><p>The positioning of product managers is particularly interesting, as Mercado Libre doesn’t see the need to have a product manager per team. Instead, it prefers all members of its delivery teams to be “product managers.” The way Mercado approaches this makes sense. They have a UX designer in every team, who coach teams to see customer perspectives. On average, this means it has one product manager for every 25–30 engineers, a structure that has worked wonderfully for them during their growth story. This structure makes sense for them, because they have a tightly aligned UX team and also ensure that they have a UX person on every team.</p><p>Its management team also strived to ensure that 90% of decisions were made by those closest to the work, with only 10% of decisions escalated to leadership and 1% of those positioned as “golden nuggets”. These are strategic pieces of decision-making that executives look at very seriously and support the direction of travel for engineering teams.</p><p>Alongside the team structure, Mercado Libre also has a robust system which helps to accelerate the team, facilitate scaling-up and drives innovation. It has a robust platform that takes care of risk scoring, machine learning and cloud computing while reducing the cognitive load on product engineering teams.</p><p>Mercado Libre’s Fury platform does exactly that, being available to all its developers, who could instantiate it with a few commands. The platform prides itself on providing a consistent developer experience, removing complexity, and offering a cheap service that allows product engineers to innovate at pace. It does so by taking care of the following behind the scenes:</p><ul><li>Software management through GitHub, Jenkins and Docker</li><li>Compute services through GCP and AWS</li><li>Data services through GCP, AWS and Oracle Cloud</li><li>Service management through New Relic, Opsgenie, Jupyter and Bugsnag</li></ul><p>One key to the Fury platform’s success outlined during the QCon session was that it followed Mercado Libre’s transition to a microservices-based architecture, meaning it invested significant time and effort to decouple its architecture before building the developer platform.</p><p>We’ve seen a trend towards clients attempting to build ‘platforms’ without paying the required attention to the coupling of their architecture. Unfortunately, it’s a recipe for failure which takes serious work to undo. Instead, organizations should approach decoupling by understanding the domains and bounded contexts within the system architecture.</p><p>Fury is a perfect case study of how it should be done, with a modern technology firm considering leadership, teaming, talent and architecture and understanding each are key to delivering success. It will be interesting to see if more businesses take such an approach having witnessed the session.</p><h4><strong>Is continuous deployment beneficial?</strong></h4><p>Another hugely popular session I attended during my time at QCon this year was a fantastic talk by representatives at Lyft on its journey to continuous deployment and the struggles it faced on the way to ensure developers could deploy code effortlessly within minutes.</p><p>At this stage it’s of course important to differentiate between continuous deployment and continuous delivery. On this, Jez Humble, author of the seminal work, Continuous Delivery, said: “…when we can release on demand at the push of a button, during normal business hours, we are doing continuous delivery.” Simply put then, continuous delivery and continuous deployment attempt to automate the entire deployment process to production. Jez also believes that “the key thing we should care about is not the form, but the outcomes: deployments should be low-risk, push-button events we can perform on demand.”</p><p>Many organizations would envy Lyft’s starting point — its developers could deploy from staging to canary to production in half a day. It’s commendable that the business has continued improving, as it outlined the following four pain points:</p><ol><li>Deploying took half a developer’s day</li><li>Many commits deployed at once</li><li>Commits from many different authors, each with various changing components</li><li>Many uncoordinated changes often made it difficult to figure out what was happening during incidents</li></ol><p>Lyft started tackling this by creating a Deploys Team, solely accountable for reaching continuous deployment to reclaim developers’ time. The key here was empowering a multi-disciplinary team to own the entire thought process of deployment. This aligns perfectly with our advice to clients on how to motive talented engineers, by providing autonomy and a clear sense of purpose.</p><p>Alongside the need to reclaim its developers’ time, Lyft supplemented the Deploys Team objective with three design tenants:</p><ol><li>Automated — remove as much manual intervention as possible</li><li>Scalable — allow teams to add integration checks and alerts to govern deployments efficiently</li><li>Responsive — the system should capture the mental context that developers have now</li></ol><p>This approach provided Lyft’s developers strong foundations to build on, while ensuring teams had smaller deploy sizes was a key metric. It was mentioned that Lyft needed to measure quicker time to production since roadmaps and feature complexities varied by teams. This is one of the infamous DORA metrics we strongly advise most clients to keep an eye on. However, Lyft started from a different launch pad so could afford not to monitor its lead time to deploy that closely.</p><p>It was also revealed that Lyft’s Deploy Team built its deploy system in-house, using the following tech stack:</p><ol><li>Backend was in Go, the frontend was in React.js</li><li>PostgreSQL — for both online storage and analytics queries</li><li>ReST APIs</li></ol><ul><li>DeployAPI tracked the deployed state</li><li>DeployView interacted with the state</li><li>AutoDeployer continuously deploys when safe</li></ul><p>When questioned on this approach, Lyft stated building a tool in-house worked well for them as it allowed full customization. It also highlights the level of empowerment offered to the Deploys Team and proves a team with autonomy and a sense of purpose can produce incredible results.</p><p>The following is a visual representation of Lyft’s fundamental data model of its deployment system — jobs.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ks2HREf7yNQ6khwynkn4Tw.png" /></figure><p>A few jobs came together to form a pipeline.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8T0w7vGuzo46KPJ-_1Cwlg.png" /></figure><p>Each pipeline would go through several states.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*X9GrunHRXMqZeeraQC81bA.png" /></figure><p>Lyft has no rollbacks built into the model and no rollback state in its state machine. A developer could trigger a deployment with an earlier build and it avoided concurrency issues. If Lyft has to rollback, it would have to pause the pipeline.</p><p>Lyft also revealed a sample checklist which helps them determine completion for a pipeline to pass from a waiting state to a running state:</p><ul><li>Has the revision passed continuous integration tests?</li><li>Have business metrics been affected?</li><li>Have integration tests passed?</li><li>Has bake time passed?</li><li>Ensure no service alerts are firing.</li><li>Would this cause an unintended rollback?</li><li>Is the owning team present and working?</li><li>Is the job more than 14 days old?</li></ul><p>This is an excellent checklist for moving from one state to another, although if considering a similar approach I recommend adding as many non-functional requirements as you need to the tests, along with code linting and some coding standards.</p><p>Lyft closed the session by outlining key lessons it learned about continuous deployment along its journey:</p><ul><li>A less delicate rollout would’ve been fine. Its developers were excited by the prospect of continuous delivery and Lyft was working to make its developers’ lives easier</li><li>Not having automated rollbacks worked well for them</li><li>Previously, it was easy for ineffective changes to ship to production — continuous deployment has significantly reduced such errors</li><li>Continuous deployment. It has made developers’ lives easier and given them more confidence to make changes across projects</li><li>Continuous deployment reduced Lyft’s exposure to CVEs. A CVE announcement used to result in a scramble; now, they use the continuous deployment pipeline with little manual work</li></ul><p>At McKinsey, we regularly tell clients that continuous deployment is a time-intensive process to introduce but is worth the investment. The journey lasted a few years but has resulted in empowered engineers with the latitude and space to innovate at pace. With backing from senior leadership teams, it pays off in the long run.</p><p><em>They are my key takeaways from just two brilliant sessions I attended at QCon 2022 on behalf of McKinsey. My colleagues will be sharing their own insight from other QCon talks in the coming weeks, so watch this space.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2be5bf75f75e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/digital-mckinsey/overheard-at-qcon-accelerating-teams-and-continuous-deployment-2be5bf75f75e">Overheard at Qcon: Accelerating teams and continuous deployment</a> was originally published in <a href="https://medium.com/digital-mckinsey">McKinsey Digital Insights</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>