<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Piet van Dongen</title>
    <description>The latest articles on DEV Community by Piet van Dongen (@pietvandongen).</description>
    <link>https://dev.to/pietvandongen</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F136849%2F0b73c223-1000-41cb-958a-507edaee17fc.jpeg</url>
      <title>DEV Community: Piet van Dongen</title>
      <link>https://dev.to/pietvandongen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pietvandongen"/>
    <language>en</language>
    <item>
      <title>Resolving the paradox of cloud-native solutions: more agility without total control</title>
      <dc:creator>Piet van Dongen</dc:creator>
      <pubDate>Tue, 31 Jan 2023 08:20:55 +0000</pubDate>
      <link>https://dev.to/aws-builders/resolving-the-paradox-of-cloud-native-solutions-more-agility-without-total-control-a08</link>
      <guid>https://dev.to/aws-builders/resolving-the-paradox-of-cloud-native-solutions-more-agility-without-total-control-a08</guid>
      <description>&lt;p&gt;&lt;strong&gt;Being cloud-native enables organizations to implement changes at the speed of light, but leveraging cloud technology also means giving away control. That sounds like a paradox, but it resolves once you realize that you don’t need to give away control completely. But what do you keep in-house, and what power do you give to your cloud technology and third-party software vendors? Sneak peek: focus your efforts on your core competitive edge while smartly integrating with the non-differentiating stuff.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t feel like reading? Watch this post (and more) as a video
&lt;/h2&gt;

&lt;p&gt;Last week, I joined an XPLUS Breakfast Session to talk about how organizations can leverage cloud technology to shorten their time-to-value. Our client Ctac, personified by Ivo van der Raad, started the webcast by presenting their cloud plans for their &lt;a href="https://www.ctacnv.com/products/xv-retail-suite/" rel="noopener noreferrer"&gt;XV Retail Suite&lt;/a&gt;. Exciting stuff, so feel free to come back here later and &lt;a href="https://www.xplus.eu/blog/webinars/free-webinar-leveraging-public-cloud-technology/" rel="noopener noreferrer"&gt;watch the session first&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I used my ten minutes to look at this typical retail challenge from a technical point of view. This blog post is an extended transcript of that presentation, with the upside that you don't have to look at my face the whole time. Win-win.&lt;/p&gt;

&lt;p&gt;Now, back to resolving the paradox.&lt;/p&gt;

&lt;h2&gt;
  
  
  The need for speed
&lt;/h2&gt;

&lt;p&gt;Before I get into the technical stuff, let me briefly explain the problem in business terms first.&lt;/p&gt;

&lt;p&gt;Consumer-facing software is moving faster than ever, with customers expecting increasingly safer, better, and faster solutions. Evolving technology is a significant enabler and driving force behind this acceleration. Successful solution providers combine tailor-made software, third-party packages, and SaaS offerings to craft the stuff their customers crave. That's impressive! But it also sounds like a magic trick. An illusion, if you will. How is it possible to hand over so much control to external parties and maintain the agility needed to compete in the market?&lt;/p&gt;

&lt;p&gt;Smart integration, that's how.&lt;/p&gt;

&lt;h2&gt;
  
  
  The integration challenge: from central management to distributed control
&lt;/h2&gt;

&lt;p&gt;Three focus areas emerge if we compare this current reality to the world of a decade or more ago.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6wj3spgsmou7nhscq98s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6wj3spgsmou7nhscq98s.png" alt="Comparison of integration solutions today versus a decade or more ago" width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first is a shift from centrally managed, self-hosted application landscapes to distributed solutions. Let's say an organization has a few systems containing customer data and many custom-made services and clients that integrate with them. A decade ago, their IT department would have self-hosted this custom and third-party software combination, employing a variety of service buses, API endpoints, and direct messaging solutions. They would have slowly evolved the landscape whenever they had to build new functionality or add a new system.&lt;/p&gt;

&lt;p&gt;Thanks to the advent of Software as a Service, IaaS, PaaS, and high-level cloud services, organizations need to move much faster to keep up. Their landscapes are fragmenting. Keeping expertise in-house is costly and reduces innovation budgets.&lt;/p&gt;

&lt;p&gt;Here emerges the second challenge. The technology market's shift to SaaS and managed cloud services, combined with the never-ending desire for better customer functionality, forces companies to gain traction and move more dynamically.&lt;/p&gt;

&lt;p&gt;That can only mean, whether they want to or not, that builders need to let go of some of the control they used to have. But they still need to integrate everything. They must learn how to leverage new ways of integrating their building blocks, and I propose they use those giant vendors' shoulders and shift a lot of the heavy lifting onto the public cloud and SaaS providers' offerings.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cloud-native integration paradox: more agility, less control
&lt;/h2&gt;

&lt;p&gt;All right, now we are looking at what seems to be a paradox. Organizations need to be in total control, but at the same time, they need to hand over some control to external parties. What gives?&lt;/p&gt;

&lt;p&gt;Organizations need to be able to move fast to respond to the changing needs of their customers. They must deliver changes into production quickly, see what works, and continually adapt based on what they learn. To do so, they require the technology and skills to precisely determine what their customers want and give it to them. They must practice deploying changes rapidly but safely. And they need to develop a software delivery process that is solid and improving over time. In other words, organizations need to be in complete control.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1oar3lp92c64axga3fad.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1oar3lp92c64axga3fad.png" alt="Diagram explaining the objective and means of an organization wanting to be competitive" width="800" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, I suggest that they hand over some control to external parties. That means they no longer have total power over all the underlying infrastructure or the services and software they need to combine into a solution.&lt;/p&gt;

&lt;p&gt;So here we have the paradox of delivering value, end-to-end, through software using the cloud and software as a service: the need for more agility while simultaneously leveraging the offerings of external vendors.&lt;/p&gt;

&lt;p&gt;Luckily, this is not an actual paradox. It is possible to increase agility while giving up control, but the trick is to be smart about it. Let me explain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build, buy, outsource, integrate? All of the above (but: pick your battles)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7yuyf7a3yj3w5a1kpco1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7yuyf7a3yj3w5a1kpco1.png" alt="A Wardley Map breaking down the components of a mobile AI-powered photo retouching app" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's look at the choices the fictional organization behind this highly successful (but fictional, like I said) app has made.&lt;/p&gt;

&lt;p&gt;In the image above, you see an example architecture of an AI-powered photo retouching app, broken down into its components and plotted on a &lt;a href="https://en.wikipedia.org/wiki/Wardley_map" rel="noopener noreferrer"&gt;Wardley Map&lt;/a&gt;. The y-axis shows us how valuable elements of the solution are to end users. The higher, the more visible a component is to them, and the more valuable they deem it: a user generally does not care if you use platform X or Y as long as their app works reliably. The x-axis is about the evolutionary stage of the components. Just-discovered technology, for example, will appear on the left, but the older it gets, the more it will shift to the right. Over a decade ago, container platforms were relatively new, so they would have been plotted much farther left than they are now.&lt;/p&gt;

&lt;p&gt;At the top left, we see the mobile application users interact with daily. It uses custom algorithms running somewhere in the backend or the app itself. This functionality is by far the most customer-visible and valuable thing they own; that’s why it’s so high on the map. So, they want complete control: they design, develop, and deploy the app and algorithms themselves.&lt;/p&gt;

&lt;p&gt;To further evolve the algorithms, their data scientists and business stakeholders need to gain continuous insight into their users' behavior. The blue dots are the systems and services that help them collect, combine, transform, and finally visualize their user's behavioral data using managed services, third-party tooling, and a hosting platform. They don't need complete control here, so they opt to hand off its management to service providers. However, they do need control over integrating this part of their landscape.&lt;/p&gt;

&lt;p&gt;Then there is the so-called boring stuff, at least from a software creator's point of view, at the bottom right in orange: the critical infrastructure. Their customers do not care one bit if they manage this themselves. But like water and electricity, it is fundamental for everything built on top to work. So, they want to exert less control here: they don't want to spend costly resources managing all this highly available, virtually infinitely scalable stuff. They want to leverage the years of experience of a party that knows how to run critical infrastructure while paying only for what they use. So they outsource this.&lt;/p&gt;

&lt;p&gt;Here we have the first part of the answer to the so-called paradox of less control and more speed. Now let's solve the integration part of this puzzle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrating the solution: less control = looser coupling
&lt;/h2&gt;

&lt;p&gt;Here is the same solution from an integration perspective.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1g0yy8xcs3cbqz6z5obh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1g0yy8xcs3cbqz6z5obh.png" alt="An example of integration options for a end-user facing mobile application with a backend and SaaS integrations" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are still looking at a mix of self-hosted applications integrated with managed, third-party services and externally hosted SaaS. At the infrastructure level, there is a mixture of public cloud, private cloud, or a private data center, and the magical SaaS infrastructure.&lt;/p&gt;

&lt;p&gt;The (still fictional) AI-powered photo retouching app company smartly combines integration solutions to make something of these mixed ingredients. The general rule of thumb here is simple: the more control they have over solution lifecycles, the tighter they can couple them. And vice versa: less control, looser coupling.&lt;/p&gt;

&lt;p&gt;On the left-hand side, there is all the stuff they control entirely. They integrate using direct connections, push notification services, and message queues.&lt;/p&gt;

&lt;p&gt;In the middle are the managed services they employ. They use a central event bus, so they decouple the integration slightly more than on the left by introducing a highly configurable, flexible, but fast integration solution in the middle. It could be a serverless event router like &lt;a href="https://aws.amazon.com/eventbridge/" rel="noopener noreferrer"&gt;Amazon Eventbridge&lt;/a&gt; or &lt;a href="https://azure.microsoft.com/en-us/products/event-grid" rel="noopener noreferrer"&gt;Azure Event Grid&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At the far end, on the right-hand side, in orange, are the external parts of our landscape: SaaS offerings, maybe another cloud, or externally hosted service. Since the photo app company has no control over them, they decouple these elements using data transfer services or an event bus at the edge of their specific hosting platforms. This way, the organization can still evolve its end-to-end solution, and the SaaS and external service providers can continuously update their offerings independently. In the case of an API or contract change, the AI photo app developers only need to change the data transformation logic, while the rest can keep running as before.&lt;/p&gt;

&lt;p&gt;Now the speed-up-with-less-control paradox is resolved. Again, we need to realize that our landscape consists of groups of subsystems, grouped by their properties — in this case, the amount of evolutionary control the organization has over each solution piece. From this perspective, they can then integrate them using fit-for-purpose integration solutions. They still control their total solution, just not all its internals.&lt;/p&gt;

&lt;p&gt;Excellent, but the story may not convince you entirely yet — the company, its users, and its success are fictional, after all. Let me try to convince you with a real-world example where I was closely involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  OHRA: cloud-native development and integration from the trenches
&lt;/h2&gt;

&lt;p&gt;OHRA, one of the Netherlands' biggest directly writing insurance companies, &lt;a href="https://www.luminis.eu/cases/ohra-cloud-migration/" rel="noopener noreferrer"&gt;asked Luminis to help migrate their application landscape from their on-premise data center to the AWS cloud&lt;/a&gt;. Their application landscape was large, with almost 1,000 integrations between internal and external services, clients, and databases. Did I mention the fixed deadline? About a year from the start. We helped them make it, partly thanks to the abovementioned integration strategy.&lt;/p&gt;

&lt;p&gt;Most of OHRA's moving parts are under their control, so they are integrated using a mix of direct messaging and message queues. When moving to AWS, we helped them mostly replatform the applications from application servers running Java applications to a managed Kubernetes cluster running containers. Much of OHRA's data flows through third-party packages spread over their application landscape. So we employed the tactic we saw on the previous slide: loose coupling for SaaS integrations using data transfer logic at the edge and asynchronous communication with third-party apps installed in their application landscape.&lt;/p&gt;

&lt;p&gt;A big help in making OHRA's cloud migration deadline was their existing service-oriented architecture, an excellent stepping stone towards a more event-driven and, thus, cloud-native architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Further reducing complexity and costs, improving agility
&lt;/h3&gt;

&lt;p&gt;After migrating, the cloud provided OHRA with new opportunities to evolve its solutions. One example is the modernization of batch applications.&lt;/p&gt;

&lt;p&gt;Previously, scheduled jobs were deployed on self-managed servers, running 24/7, even though most of them only actively did something for a few minutes to a couple of hours a day. &lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/framework/cost-dp.html" rel="noopener noreferrer"&gt;In the public cloud, you should pay only for what you use&lt;/a&gt;, so I helped OHRA envision a more cloud-native and cost-effective way of creating short-running jobs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkxlk5bap1spjh39sahho.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkxlk5bap1spjh39sahho.png" alt="Comparison of two solutions, looking at lines of code, code complexity, costs per job run, and resource usage" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;During two weeks, I led a team that re-architected an existing solution and delivered a modern, lean batch application: a distributed data processor made from a combination of serverless building blocks like &lt;a href="https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-create-rule-schedule.html" rel="noopener noreferrer"&gt;Amazon EventBridge rules&lt;/a&gt;, &lt;a href="https://aws.amazon.com/sqs/" rel="noopener noreferrer"&gt;Amazon SQS queues&lt;/a&gt;, and &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS Lambda Functions&lt;/a&gt;. As you can see in the image above, this drastically reduced the amount of code needed (and thus its complexity), the resources used, and the costs accrued.&lt;/p&gt;

&lt;p&gt;OHRA can use this cloud-native way of working moving forward and enable their teams to free up time and resources, which they can then spend on stuff that will help the business grow its competitive edge.&lt;/p&gt;

&lt;h2&gt;
  
  
  One more time: the paradox resolved
&lt;/h2&gt;

&lt;p&gt;So, now you have a complete, high-overview answer to the faux paradox of gaining speed while not having complete control. Allow me to recap.&lt;/p&gt;

&lt;p&gt;First off, from a market perspective, companies are forced to move away from centrally controlled, neatly contained solutions running in their data centers towards integrating their locally managed custom building blocks with third-party software and externally hosted SaaS solutions.&lt;/p&gt;

&lt;p&gt;There is a two-part solution to this problem.&lt;/p&gt;

&lt;p&gt;The first part concerns being smart about spending valuable time and resources. Organizations require complete control of the software development and deployment lifecycle for the user-facing features they develop. Everything that powers this end-to-end solution — but is less visible or even completely invisible to the customer — can be hired as a service or bought as a product from a cloud provider.&lt;/p&gt;

&lt;p&gt;The other half of the answer concerns being smart about integrating a fragmented landscape. I identified three groups, from fully controllable and evolvable elements to managed services and non-transparent SaaS solutions. Tight coupling is a good solution for distributed applications under a company’s control, but managed services and SaaS need looser coupling. Organizations can implement this solution by leveraging a combination of message queues, event buses, and data transfer services.&lt;/p&gt;

&lt;p&gt;And thus, the paradox is dissolved!&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>devops</category>
      <category>serverless</category>
    </item>
    <item>
      <title>The Evolution of AWS from a Cloud-Native Development Perspective: Serverless, Event-Driven, Developer-Friendly, Sustainable</title>
      <dc:creator>Piet van Dongen</dc:creator>
      <pubDate>Fri, 18 Mar 2022 13:51:34 +0000</pubDate>
      <link>https://dev.to/aws-builders/the-evolution-of-aws-from-a-cloud-native-development-perspective-serverless-event-driven-developer-friendly-sustainable-45go</link>
      <guid>https://dev.to/aws-builders/the-evolution-of-aws-from-a-cloud-native-development-perspective-serverless-event-driven-developer-friendly-sustainable-45go</guid>
      <description>&lt;p&gt;Authors: &lt;a href="https://www.luminis.eu/expert/auke-noppe/" rel="noopener noreferrer"&gt;Auke Noppe&lt;/a&gt; and Piet van Dongen&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recent AWS announcements feature fewer service additions and instead underline the efforts the cloud giant is undertaking to increase the strength of its massive portfolio of building blocks. AWS CEO Adam Selipsky has mapped out a course that intensifies AWS’s focus on serverless technologies, event-driven architectures, an improved developer experience, and sustainability.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F07u7h40ztg6xn80kinut.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F07u7h40ztg6xn80kinut.png" alt="Serverless, Event-Driven, Developer-Friendly, Sustainable"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this blog post, we outline the direction cloud-native engineering is heading in, how you can learn to leverage AWS’s portfolio of managed services and primitives to your advantage, and how cloud technology is reshaping the field of software engineering. &lt;/p&gt;

&lt;p&gt;If you prefer watching a video, we got you covered! We recently broadcasted a cloud-native development focussed AWS re:Invent re:Cap featuring most of this article’s themes. You can find the recording at the end of this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  What AWS is telling us
&lt;/h2&gt;

&lt;p&gt;With so many services in its portfolio already, &lt;a href="https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2021/" rel="noopener noreferrer"&gt;this year’s AWS re:Invent didn’t hold any massively surprising announcements&lt;/a&gt;. With the yearly conference reaching its 10-year milestone — and AWS being around for 15 years — the AWS platform is maturing while at the same time evolving to meet market demands.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS leadership’s re:Invent keynotes
&lt;/h3&gt;

&lt;p&gt;Freshly at the helm of AWS, &lt;a href="https://www.youtube.com/watch?v=WGA2P_oH5Xc" rel="noopener noreferrer"&gt;Adam Selipsky held his first re:Invent Keynote&lt;/a&gt;. Selipsky’s primary message: AWS is shifting from merely offering infrastructure primitives to embracing the idea of being a so-called &lt;a href="https://www.forbes.com/sites/ronshevlin/2021/05/17/jpmorgan-chase-the-amazon-web-services-of-fintech-partnerships/?sh=7278c6a9a83d" rel="noopener noreferrer"&gt;platform of platforms&lt;/a&gt;. Therefore, AWS’s offerings will increasingly become an interesting piece of organizational value chains. &lt;/p&gt;

&lt;p&gt;While Amazon CTO Werner Vogels took the stage after an introduction referencing Fear and Loathing in Las Vegas, &lt;a href="https://www.youtube.com/watch?v=8_Xs8Ik0h1w" rel="noopener noreferrer"&gt;his keynote&lt;/a&gt; wasn’t as spectacular. Dr. Vogels took the time to make a case for &lt;a href="https://aws.amazon.com/architecture/well-architected/" rel="noopener noreferrer"&gt;Well-Architected applications&lt;/a&gt;, which we interpret as a good signal for builders. Other highlights were the release of &lt;a href="https://constructs.dev/" rel="noopener noreferrer"&gt;Construct Hub&lt;/a&gt; and the &lt;a href="https://cdkpatterns.com/" rel="noopener noreferrer"&gt;CDK Patterns library&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg015aeptllfnsa25o5nh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg015aeptllfnsa25o5nh.png" alt="Werner Vogels on stage talking about primitives, not frameworks"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Primitives, not frameworks
&lt;/h3&gt;

&lt;p&gt;An important theme these last few months was the expansion and refinement of AWS as a cloud-native platform. You can view AWS as a data center containing compute resources and seemingly limitless storage at the other end of the internet, or maybe as a big bunch of infrastructural primitives. The way we look at the platform is by grouping its services into three categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Infrastructural primitives&lt;/strong&gt;: storage, networking, and compute (a.k.a. Infrastructure as a Service, IaaS).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Middleware-oriented services&lt;/strong&gt;: web servers, container orchestrators, databases, message queues (a.k.a. Platform as a Service, PaaS).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Serverless&lt;/strong&gt;: take what you need, pay-as-you-go.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building something on a platform containing 200+ services might seem a bit overwhelming. Therefore, from the perspective of cloud-native development, we use this rule of thumb: start building from the most modern collection of services first and, if needed, complement the solution using more primitive building blocks. &lt;/p&gt;

&lt;p&gt;Frequently, you will end up with a serverless solution that is rapidly and almost infinitely scalable and highly cost-effective. However, sometimes there are missing pieces of the puzzle, and you can’t complete it with serverless services. We can then turn to platform primitives or infrastructure services like containers, RDS, virtual machines, or even complete SaaS solutions. Don’t forget to update your solutions from time to time, as the platform never stops evolving.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reduce risk, improve performance, grow revenue, increase efficiency
&lt;/h3&gt;

&lt;p&gt;Effectively adopting the cloud means &lt;a href="https://dev.to/?p=30362"&gt;making the cloud work for your money&lt;/a&gt;. It’s not just a virtualized data center, or at least: that’s not the mindset that enables you to accelerate your organization towards its goals. Cloud-native development requires understanding cloud-native best practices in terms of performance, resiliency, cost optimization, and security. Luckily, AWS keeps investing in &lt;a href="https://aws.amazon.com/professional-services/CAF/" rel="noopener noreferrer"&gt;adoption frameworks&lt;/a&gt;, &lt;a href="https://aws.amazon.com/architecture/well-architected/" rel="noopener noreferrer"&gt;its Well-Architected Framework&lt;/a&gt;, and &lt;a href="https://aws.amazon.com/certification/" rel="noopener noreferrer"&gt;certification&lt;/a&gt; and &lt;a href="https://www.aws.training/" rel="noopener noreferrer"&gt;training&lt;/a&gt; programs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fenkx2jrykrokpoltyblo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fenkx2jrykrokpoltyblo.png" alt="serverless"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  The ongoing evolution of serverless: commoditize all the things
&lt;/h2&gt;

&lt;p&gt;You can immediately tell the serverless category from above is something special. Its services allow you to focus on functional value without worrying about what is needed to run it. &lt;a href="https://www.youtube.com/watch?v=b7Nc_FJiosk" rel="noopener noreferrer"&gt;You can compare a serverless service to a commodity&lt;/a&gt;, like electricity: you use it anytime you need it, without much second thought. For a while, serverless was synonymous with functions (FaaS) or Lambda, but its reach rapidly extends beyond compute to data stores and application integration. &lt;/p&gt;

&lt;p&gt;Serverless technology keeps gaining traction, as we can see in its continuous evolution. Let’s look at AWS’s current serverless portfolio and see how it evolves.&lt;/p&gt;

&lt;h3&gt;
  
  
  Three categories of building blocks
&lt;/h3&gt;

&lt;p&gt;Roughly speaking, we can divide AWS’s serverless building blocks into three categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Compute&lt;/strong&gt;: running code without provisioning servers (&lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;Lambda&lt;/a&gt;, &lt;a href="https://aws.amazon.com/fargate/" rel="noopener noreferrer"&gt;Fargate&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Data Storage&lt;/strong&gt;: storing objects, documents, relational and other data (&lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;Simple Storage Service (S3)&lt;/a&gt;, &lt;a href="https://aws.amazon.com/dynamodb" rel="noopener noreferrer"&gt;DynamoDB&lt;/a&gt;, &lt;a href="https://aws.amazon.com/rds/proxy/" rel="noopener noreferrer"&gt;Relational Database Service Proxy&lt;/a&gt;, &lt;a href="https://aws.amazon.com/rds/aurora/serverless/" rel="noopener noreferrer"&gt;Aurora Serverless&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;(Application) integration&lt;/strong&gt;: &lt;a href="https://aws.amazon.com/eventbridge/" rel="noopener noreferrer"&gt;EventBridge&lt;/a&gt;, &lt;a href="https://aws.amazon.com/step-functions/" rel="noopener noreferrer"&gt;Step Functions&lt;/a&gt;, &lt;a href="https://aws.amazon.com/sqs/" rel="noopener noreferrer"&gt;Simple Queue Service (SQS)&lt;/a&gt;, &lt;a href="https://aws.amazon.com/sns/" rel="noopener noreferrer"&gt;Simple Notification Service (SNS)&lt;/a&gt;, &lt;a href="https://aws.amazon.com/api-gateway/" rel="noopener noreferrer"&gt;API Gateway&lt;/a&gt;, &lt;a href="https://aws.amazon.com/appsync/" rel="noopener noreferrer"&gt;AppSync&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Combining these building blocks, you can quickly create valuable solutions at scale.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkf6qoyk8dq26i47lqtu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkf6qoyk8dq26i47lqtu.png" alt="Werner Vogel on stage announcing the next frontier"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Serverless expansion areas
&lt;/h3&gt;

&lt;p&gt;While SaaS is powerful and often offers excellent value for money, it has significant constraints in terms of flexibility. To win in the marketplace, we need more flexibility for our differentiating technology. These are the lines along which serverless is evolving. Its journey started at compute, but the serverless philosophy quickly moved into other AWS categories.&lt;/p&gt;

&lt;h4&gt;
  
  
  Big data and streaming
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/11/amazon-msk-serverless-public-preview/" rel="noopener noreferrer"&gt;Amazon MSK will get a serverless counterpart&lt;/a&gt;, which is great news for organizations running Kafka workloads. The same goes for AWS’s big data platform &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/11/amazon-emr-serverless-preview/" rel="noopener noreferrer"&gt;Amazon EMR&lt;/a&gt; and its data warehouse offering &lt;a href="https://aws.amazon.com/blogs/aws/introducing-amazon-redshift-serverless-run-analytics-at-any-scale-without-having-to-manage-infrastructure/" rel="noopener noreferrer"&gt;Amazon Redshift&lt;/a&gt;. Amazon Kinesis is also evolving its data streaming service, exemplified by the &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/11/amazon-kinesis-data-streams-on-demand/" rel="noopener noreferrer"&gt;new Data Streams On-Demand feature&lt;/a&gt;. Last but certainly not least: &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/11/aws-data-exchange-for-apis/" rel="noopener noreferrer"&gt;AWS Data Exchange for APIs&lt;/a&gt; makes it a breeze to use third-party data while leveraging AWS-native authentication and governance.&lt;/p&gt;

&lt;h4&gt;
  
  
  Application integration
&lt;/h4&gt;

&lt;p&gt;Apart from &lt;a href="https://twitter.com/julian_wood/status/1465842874457763840" rel="noopener noreferrer"&gt;getting extra ephemeral storage&lt;/a&gt;, AWS Lambda now supports &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/11/aws-lambda-partial-batch-response-sqs-event-source/" rel="noopener noreferrer"&gt;partial batch responses for SQS&lt;/a&gt; and &lt;a href="https://aws.amazon.com/blogs/compute/filtering-event-sources-for-aws-lambda-functions/" rel="noopener noreferrer"&gt;event source filtering for Kinesis, DynamoDB, and SQS&lt;/a&gt;. EventBridge added &lt;a href="https://aws.amazon.com/blogs/aws/new-use-amazon-s3-event-notifications-with-amazon-eventbridge/" rel="noopener noreferrer"&gt;Amazon S3 Notifications&lt;/a&gt;. On the surface, that might sound like a small change. But as Matt Coulter puts it: &lt;a href="https://mobile.twitter.com/NIDeveloper/status/1469659751235657735" rel="noopener noreferrer"&gt;code is a liability&lt;/a&gt;, not an asset. Anything you can push to the cloud vendor gains you some security and an increased ability to focus on value. You can now control Amazon Athena workflows using &lt;a href="https://aws.amazon.com/blogs/compute/visualizing-aws-step-functions-workflows-from-the-amazon-athena-console/" rel="noopener noreferrer"&gt;AWS Step Functions&lt;/a&gt; in that same vein.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are applications, infrastructure, and integration separable?
&lt;/h3&gt;

&lt;p&gt;Now that serverless is moving beyond compute into the data and integration layers, we might ask ourselves: are applications, infrastructure, and integration separable? Is programming still mostly about functions and logic, and where does it bleed into the domain of operations? The advent of serverless is pushing boundaries everywhere and blurring previously clear and stable lines. &lt;/p&gt;

&lt;p&gt;That’s excellent news, in our opinion: the more control and flexibility our teams have, the faster we can innovate. &lt;a href="https://www.enterpriseintegrationpatterns.com/ramblings/loanbroker_cdk.html" rel="noopener noreferrer"&gt;Gregor Hohpe, an Enterprise Strategist at AWS,&lt;/a&gt; says: “I firmly believe that we are just starting to realize the true potential of cloud automation. Modern cloud automation isn't just about reducing toil. (…) it can help us blur the line between application and automation code.” We agree wholeheartedly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftobyk3k3g9u485px0ehl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftobyk3k3g9u485px0ehl.png" alt="event-driven"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The future (of cloud) is event-driven
&lt;/h2&gt;

&lt;p&gt;Another direction cloud technology developments are pointing towards is that of event-driven architectures. Not only is the cloud itself highly event-driven, but applications built on cloud platforms also tend to be fitting most naturally when using the power of events. Why is that?&lt;/p&gt;

&lt;h3&gt;
  
  
  Agility in architecture
&lt;/h3&gt;

&lt;p&gt;Market forces are driving organizations to become more agile. As Jeff Bezos puts it: “&lt;a href="https://aws.amazon.com/executive-insights/content/cloud-for-ceos/" rel="noopener noreferrer"&gt;The only sustainable advantage you can have over others is agility, that’s it.&lt;/a&gt;” Organizations must learn to leverage technology to respond to and drive change to fulfill this business requirement. Event-driven architectures present the necessary characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Loosely coupled&lt;/strong&gt;: heterogeneous services and share information effectively while hiding implementation details.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Scalable&lt;/strong&gt;: multiple types of consumers can work in parallel, massively if needed.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Independent&lt;/strong&gt;: evolution, scaling, and failing can happen in isolation, especially when using buffers like event routers.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Auditable&lt;/strong&gt;: if events are routed and stored centrally, we can quickly implement auditing, policy enforcement, and information access control.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Frugal&lt;/strong&gt;: if we push changes when available, we don’t need to waste resources continuously polling for changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Event-driven systems enable us to focus on system behavior and temporality within a business domain, freeing us from the chains of rigid structures incapable of dealing with changing needs and requirements. There is a trade-off (of course): agile systems are complex and thus force us to learn to manage — seeming, but not actual — chaos.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0rvj8efmn2gu5mcfx701.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0rvj8efmn2gu5mcfx701.png" alt="Service-oriented death stars"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS building blocks for event-driven architectures
&lt;/h3&gt;

&lt;p&gt;Now, how is AWS helping us compose these systems? Whether you are building event-sourced systems, leverage CQRS, or are just notifying components, AWS has the building blocks you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Producers&lt;/strong&gt;: most AWS primitives, especially serverless ones, produce events. For example, &lt;a href="https://docs.aws.amazon.com/whitepapers/latest/security-overview-aws-lambda/lambda-event-sources.html" rel="noopener noreferrer"&gt;we can feed Lambda with events from API Gateway, CloudWatch, DynamoDB, S3, Kinesis, and many more services&lt;/a&gt;. Besides that, we can use custom apps, SaaS solutions, and microservices as event producers.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Consumers&lt;/strong&gt;: notable consumers are AWS Lambda, Amazon SQS, Amazon Kinesis Data Firehose, and Amazon SNS. We can also use APIs from SaaS offerings and custom apps as event consumers.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Routers&lt;/strong&gt;: we can fulfill most integration needs using &lt;a href="https://aws.amazon.com/eventbridge/" rel="noopener noreferrer"&gt;Amazon EventBridge&lt;/a&gt; and &lt;a href="https://aws.amazon.com/sns/" rel="noopener noreferrer"&gt;Amazon SNS&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Well-Architected&lt;/strong&gt;: the &lt;a href="https://aws.amazon.com/architecture/well-architected/" rel="noopener noreferrer"&gt;AWS Well-Achitected Framework&lt;/a&gt; describes best practices, which we can assess and monitor using the &lt;a href="https://aws.amazon.com/well-architected-tool/" rel="noopener noreferrer"&gt;AWS Well-Architected Tool&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Combining these AWS services and using them to integrate SaaS and self-built products gives us the leverage we miss when only using off-the-shelf products.&lt;/p&gt;

&lt;h3&gt;
  
  
  How are event-driven cloud architectures evolving?
&lt;/h3&gt;

&lt;p&gt;The answer to this question is short and straightforward: along the same lines as serverless. Last December, AWS mostly announced incremental improvements. But they quickly add up, accumulating in an ever-more powerful platform to build and evolve our solutions on. And the announcements don’t stop when re:Invent is done: AWS is working to improve its event-driven primitives year-round. &lt;/p&gt;

&lt;p&gt;Amazon Eventbridge is an excellent example of this continuous investment. It was introduced in mid-2019 and has gained a lot of features since then: a schema registry, archiving and replaying of events, and a long list of event destinations. Its evolving event pattern matching, filtering, and routing options embody the ‘code is liability’ philosophy, enabling us to focus on value delivery. Recently, &lt;a href="https://aws.amazon.com/blogs/aws/new-use-amazon-s3-event-notifications-with-amazon-eventbridge/" rel="noopener noreferrer"&gt;S3 Event Notifications were added to EventBridge’s features&lt;/a&gt;, giving us more direct, reliable, and developer-friendly options when responding to S3 object changes. For the web-scale organizations among us, AWS introduced &lt;a href="https://aws.amazon.com/blogs/compute/introducing-cross-region-event-routing-with-amazon-eventbridge/" rel="noopener noreferrer"&gt;cross-region event routing&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;We mentioned &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventfiltering.html" rel="noopener noreferrer"&gt;Lambda’s new message filtering features&lt;/a&gt; above but want to highlight them again since they underline AWS’s continued investment in this area. Lastly, &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/11/aws-iot-twinmaker-build-digital-twins/" rel="noopener noreferrer"&gt;AWS IoT TwinMaker&lt;/a&gt; deserves some attention: it utilizes events as data sources and enables developers to create digital twins of real-world systems, which is nothing less than fantastic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs7zp60nmjyhz7osy8vvn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs7zp60nmjyhz7osy8vvn.png" alt="developer-friendly"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Improving the developer experience
&lt;/h2&gt;

&lt;p&gt;AWS provides us with many building blocks, but using them effectively is not straightforward. Luckily for us, AWS seems to understand this problem and is steadily improving in that area.&lt;/p&gt;

&lt;h3&gt;
  
  
  Programmable everything: APIs, SDKs, and the AWS CDK
&lt;/h3&gt;

&lt;p&gt;In 2002, Jeff Bezos's so-called &lt;a href="https://chrislaing.net/blog/the-memo/" rel="noopener noreferrer"&gt;API Mandate&lt;/a&gt; forced all Amazon teams to expose their data and functionality through service interfaces. Amazon has built AWS around the same principles: every service is programmatically controllable, from starting a virtual machine to &lt;a href="https://docs.aws.amazon.com/ground-station/latest/APIReference/API_GetSatellite.html" rel="noopener noreferrer"&gt;accessing a satellite&lt;/a&gt;. While this is an essential property of an effective cloud platform, it is not necessarily developer-friendly. &lt;/p&gt;

&lt;p&gt;By now, AWS has incrementally and significantly improved in this space. Besides using &lt;a href="https://docs.aws.amazon.com/general/latest/gr/aws-service-information.html" rel="noopener noreferrer"&gt;their APIs&lt;/a&gt;, we can control services and infrastructure with a &lt;a href="https://aws.amazon.com/cli/" rel="noopener noreferrer"&gt;unified Command Line Interface (CLI)&lt;/a&gt;, &lt;a href="https://aws.amazon.com/tools/" rel="noopener noreferrer"&gt;Software Development Kits (SDK)&lt;/a&gt;, &lt;a href="https://aws.amazon.com/cloudformation/" rel="noopener noreferrer"&gt;CloudFormation&lt;/a&gt;, and, since July 2019, the &lt;a href="https://aws.amazon.com/cdk/" rel="noopener noreferrer"&gt;AWS Cloud Development Kit (CDK)&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The CDK had a massive impact on developer productivity and satisfaction. While teams could already access and control services and infrastructure using the AWS SDK and their favorite programming language, infrastructure was primarily defined using &lt;a href="https://s3.us-west-2.amazonaws.com/cloudformation-templates-us-west-2/EC2InstanceWithSecurityGroupSample.template" rel="noopener noreferrer"&gt;incredible amounts of mostly punctuation marks and whitespace&lt;/a&gt;, also known as YAML or JSON. CDK — initially only flavored TypeScript and Python — finally gave developers an AWS-native means to define &lt;a href="https://twitter.com/ghohpe/status/1496314103824097283" rel="noopener noreferrer"&gt;Infrastructure as actual Code&lt;/a&gt;. Since its introduction, AWS has added support for more languages, like Java, C#, and Go.&lt;/p&gt;

&lt;h3&gt;
  
  
  Metrics, toolsets, and communities
&lt;/h3&gt;

&lt;p&gt;The lines between infrastructure and logical code have thus been blurred, creating more opportunities to increase business agility. And AWS provides more tools to increase development productivity, like metrics and purpose-built toolsets. New developments in this space are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Community support platforms like the newly announced &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/12/aws-repost-community-question-answer-service/" rel="noopener noreferrer"&gt;re:Post&lt;/a&gt; and &lt;a href="https://aws.amazon.com/blogs/aws/announcing-general-availability-of-construct-hub-and-aws-cloud-development-kit-version-2/" rel="noopener noreferrer"&gt;Construct Hub&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;  Monitoring improvements like &lt;a href="https://aws.amazon.com/blogs/aws/cloudwatch-rum/" rel="noopener noreferrer"&gt;CloudWatch Real-User Monitoring&lt;/a&gt; and &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/11/amazon-cloudwatch-metrics-insights-preview/" rel="noopener noreferrer"&gt;Metrics Insights&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;  A &lt;a href="https://aws.amazon.com/blogs/aws/codeguru-reviewer-secrets-detector-identify-hardcoded-secrets/" rel="noopener noreferrer"&gt;Secrets Detector&lt;/a&gt; addition to the software engineer’s best non-human programming buddy, Amazon CodeGuru.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ihtyw6xwpsj1qufj93i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ihtyw6xwpsj1qufj93i.png" alt="Matt Coulter on stage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Reducing complexity
&lt;/h3&gt;

&lt;p&gt;Another way to increase (business) agility is by &lt;a href="https://itrevolution.com/complexity-the-enemy-of-agility-and-execution/" rel="noopener noreferrer"&gt;reducing complexity&lt;/a&gt;. AWS seems very aware of the complexity we add when developing cloud-native solutions. It has released several offerings and improvements to its platform over the last period in reaction to this.&lt;/p&gt;

&lt;h4&gt;
  
  
  Less handcrafted code
&lt;/h4&gt;

&lt;p&gt;We can reduce the amount of hand-crafted code we deploy in several areas (once more: code is a liability!). Business analysts can kickstart machine learning workloads using &lt;a href="https://aws.amazon.com/blogs/aws/announcing-amazon-sagemaker-canvas-a-visual-no-code-machine-learning-capability-for-business-analysts/" rel="noopener noreferrer"&gt;Amazon SageMaker Canvas&lt;/a&gt;, while others can visually create full-stack apps using &lt;a href="https://aws.amazon.com/amplify/studio/" rel="noopener noreferrer"&gt;Amplify Studio&lt;/a&gt;. More incremental improvements are &lt;a href="https://aws.amazon.com/blogs/aws/enhanced-dlq-management-sqs/" rel="noopener noreferrer"&gt;enhanced dead-letter queue management for SQS&lt;/a&gt; and the Lambda and S3 enhancements mentioned earlier.&lt;/p&gt;

&lt;h4&gt;
  
  
  Feature flags and dark launches
&lt;/h4&gt;

&lt;p&gt;Reducing operational complexity is a way to increase productivity. Modern organizations have learned to leverage feature flags and dark launches to test changes without introducing much operational overhead. Our colleague Nico Krijnen took the time to write down &lt;a href="https://dev.to/?p=30557"&gt;how Amazon CloudWatch Evidently and AWS AppConfig Feature Flags can help us in this regard&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Migration automation
&lt;/h4&gt;

&lt;p&gt;Lastly, a powerful — but often not so straightforward — way to reduce complexity is to migrate existing workloads from the old data center to the cloud. AWS has been investing a lot of effort in this space and continues to do so, as evidenced by the recent releases of &lt;a href="https://aws.amazon.com/blogs/aws/new-aws-migration-hub-refactor-spaces-helps-to-incrementally-refactor-your-applications/" rel="noopener noreferrer"&gt;AWS Migration Hub Refactor Spaces&lt;/a&gt;, &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/11/introducing-aws-mainframe-modernization/" rel="noopener noreferrer"&gt;AWS Mainframe Modernisation&lt;/a&gt;, and &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/11/aws-microservice-extractor-net/" rel="noopener noreferrer"&gt;AWS MicroService Extractor for .NET&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4o6r41dr9sogp0liuqsi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4o6r41dr9sogp0liuqsi.png" alt="sustainable"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sustainability of the cloud, sustainability in the cloud
&lt;/h2&gt;

&lt;p&gt;Software engineering has more dimensions than speed and complexity. With the advent of DevOps, &lt;a href="https://dev.to/?p=31111"&gt;FinOps&lt;/a&gt;, and the increased awareness of IT infrastructure's impact on our environment, teams are increasingly responsible for more than just developing and deploying code. &lt;/p&gt;

&lt;p&gt;Adrian Cockcroft, Amazon’s newly appointed VP of Sustainability Architecture, recently headed a very insightful talk on &lt;a href="https://www.youtube.com/watch?v=3-Zq2W1-odU" rel="noopener noreferrer"&gt;architecting for sustainability&lt;/a&gt;. Amazon has committed to &lt;a href="https://sustainability.aboutamazon.com/environment/sustainable-operations/renewable-energy?energyType=true" rel="noopener noreferrer"&gt;net-zero carbon by 2040&lt;/a&gt;, and AWS aims to use &lt;a href="https://sustainability.aboutamazon.com/environment/the-cloud?energyType=true" rel="noopener noreferrer"&gt;100% renewable energy by 2025&lt;/a&gt;. AWS needs their customer’s help to decrease their footprint, so AWS has introduced several sustainability tools and improvements to their platform. &lt;/p&gt;

&lt;p&gt;The &lt;a href="https://aws.amazon.com/blogs/aws/new-customer-carbon-footprint-tool/" rel="noopener noreferrer"&gt;Customer Carbon Footprint Tool&lt;/a&gt; was released earlier this month, enabling AWS users to calculate the carbon emissions their workloads produce now and in the future. Reducing them is the next logical step, which is why AWS added a new &lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/sustainability-pillar/sustainability-pillar.html" rel="noopener noreferrer"&gt;Sustainability Pillar&lt;/a&gt; to their Well-Architected Framework. More concretely, teams optimize resource usage by choosing several &lt;a href="https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2021/" rel="noopener noreferrer"&gt;new CPU instances&lt;/a&gt; for EC2 &lt;a href="https://aws.amazon.com/blogs/aws/aws-lambda-functions-powered-by-aws-graviton2-processor-run-your-functions-on-arm-and-get-up-to-34-better-price-performance/" rel="noopener noreferrer"&gt;or Lambda&lt;/a&gt; or by analyzing the &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/11/aws-compute-optimizer-enhanced-infrastructure-metrics-ec2-instances/" rel="noopener noreferrer"&gt;enhanced AWS Compute Optimizer infrastructure metrics&lt;/a&gt; and acting accordingly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2uwjz3kfd8m3hizm91xo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2uwjz3kfd8m3hizm91xo.png" alt="Werner Vogels on stage talking about sustainability"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Survival of the fittest
&lt;/h2&gt;

&lt;p&gt;AWS’s cloud platform is becoming more mature, stable, developer-friendly, and sustainable while at the same time evolving rapidly to meet emerging business needs. By closely following the needs of its users and experimenting and growing with them, AWS keeps delivering the platform of the future. &lt;/p&gt;

&lt;p&gt;For us cloud-native developers, that’s excellent news. Increasing business agility by creating and evolving event-driven, serverless, sustainable systems that can turn on a dime in response to customer needs: it’s what we need now and going forward. &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Si0US0GJysM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>aws</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Silver Linings, No Silver Bullets: 3 Perspectives on Innovating with Cloud</title>
      <dc:creator>Piet van Dongen</dc:creator>
      <pubDate>Wed, 15 Dec 2021 09:48:08 +0000</pubDate>
      <link>https://dev.to/aws-builders/silver-linings-no-silver-bullets-3-perspectives-on-innovating-with-cloud-525b</link>
      <guid>https://dev.to/aws-builders/silver-linings-no-silver-bullets-3-perspectives-on-innovating-with-cloud-525b</guid>
      <description>&lt;p&gt;Authors: &lt;a href="https://www.luminis.eu/expert/auke-noppe/"&gt;Auke Noppe&lt;/a&gt; and Piet van Dongen&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We’ve heard the phrase, seen the cynical stickers: “There is no cloud; it’s just someone else’s computer.” That might have been true 15 years ago, but the thing we call the cloud has evolved significantly since. We live in times of accelerating change; organizations that can leverage the cloud effectively will survive them, even thrive in them. How well prepared are you?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Taking great advantage of the cloud requires a shift in thinking: technology no longer belongs to the IT department (if it ever did). To become cloud natives, organizations need to learn to streamline their effort around everything the cloud offers.&lt;/p&gt;

&lt;p&gt;Let’s look at this paradigm shift from several perspectives: operations, development, and leadership. Each of these perspectives starts with describing the ineffective way of working. We use the term &lt;strong&gt;cloud naive&lt;/strong&gt; (as in: &lt;a href="https://www.merriam-webster.com/dictionary/naive"&gt;inexperienced or lacking knowledge&lt;/a&gt;). Or as the &lt;a href="https://cloud.google.com/blog/products/devops-sre/announcing-dora-2021-accelerate-state-of-devops-report"&gt;State of DevOps report&lt;/a&gt; classifies it: the way of the low performers. Then, we cross the chasm and look at it from the &lt;strong&gt;cloud-native&lt;/strong&gt; perspective, as used by companies that use cloud technology to accelerate their business continuously.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZEQnCjqH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ih975sbupkjerztya19w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZEQnCjqH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ih975sbupkjerztya19w.png" alt="Two male relay racers running" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Operations: embrace speed to reduce risk
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Low performance: cloud naive operations
&lt;/h3&gt;

&lt;p&gt;In simpler times, developers delivered complete software packages to operations people, who in turn installed and ran them on the appropriate infrastructure. Preferably not too often and with as little change needed as possible. A similar rule determined a snail’s pace of change for platforms: standardization before innovation. Why? Because every change introduced risk and operations were held responsible for it, often having to clean up the mess when things went wrong.&lt;/p&gt;

&lt;p&gt;This model led operations departments to push back hard on changes and reduce the speed of change—developers adjusted by increasing release sizes and time between releases.&lt;/p&gt;

&lt;p&gt;The result: slow change, little cooperation, suffering users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Elite performance: cloud-native operations
&lt;/h3&gt;

&lt;p&gt;The way out of this standoff is well-known by now but not always well understood or effectively implemented: &lt;a href="https://newrelic.com/blog/nerd-life/devops-name"&gt;DevOps&lt;/a&gt;. Making agile teams responsible for business outcomes forces down the classical barriers between the business, developers, and operations, leading to better customer outcomes. Cloud platforms have evolved by adding layer upon layer of abstraction onto the classic data center, leading to &lt;a href="https://aws.amazon.com/products/"&gt;hundreds of managed services&lt;/a&gt; that teams can continuously leverage to decrease their time-to-value. Perhaps counterintuitively, this increased rate of change enables teams to manage risk better than ever.&lt;/p&gt;

&lt;p&gt;Think about it: if &lt;a href="https://aws.amazon.com/executive-insights/content/cloud-for-ceos/"&gt;time-to-value is the most critical metric for success&lt;/a&gt;, change size logically also gets smaller. Minor changes are easier to manage in case of failure, and deploying multiple times a day yields teams totally in control of their technological solutions.&lt;/p&gt;

&lt;p&gt;This cloud-native way of doing operations yields precisely what these teams aimed for in the first place: &lt;a href="https://aws.amazon.com/blogs/enterprise-strategy/please-do-not-take-more-risks/"&gt;less risk, more control&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BJ4yoXRB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cx3ebiwugo37pcwp55vh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BJ4yoXRB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cx3ebiwugo37pcwp55vh.png" alt="Two female relay racers running" width="800" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Development: focus on outcomes, not technology
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Low performance: cloud naive development
&lt;/h3&gt;

&lt;p&gt;Before the emergence of the cloud, servers had to be installed by hand, as did the software running on them. Installing and testing releases was a time-consuming process, mostly done by other teams. So, developers made sure their sparsely released work contained as many features as possible. Any infrastructural requirements and other dependencies had to be carefully communicated and planned with IT operations, dictating long lead times between innovative ideas and customer value.&lt;/p&gt;

&lt;p&gt;As a result, developers preferred working with stable, minimally variable environments simulated on their development machines. Their products were massive monoliths often written using a single programming language, running on single application container instances. Development feedback might have been fast; customer feedback certainly wasn’t. Developers were often kept totally in the dark about the impact of their changes in production until nightly calls from frustrated IT operations, who spoke in an entirely different IT dialect.&lt;/p&gt;

&lt;p&gt;Again: slow change, little cooperation, suffering users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Elite performance: cloud-native development
&lt;/h3&gt;

&lt;p&gt;The step-by-step virtualization and containerization of servers, platforms, and runtime environments have enabled development teams to master what was previously the sole job of operations. And more importantly, the marriage of Dev and Ops has given birth to beautiful things like &lt;a href="https://martinfowler.com/bliki/InfrastructureAsCode.html"&gt;infrastructure as code&lt;/a&gt;, CI/CD, and &lt;a href="https://sre.google/"&gt;SRE&lt;/a&gt;. Managing complex systems this way has opened up opportunities for creating massively scalable but well-observable systems. The old monoliths have been split into small, modular services or even single functions that typically communicate asynchronously using a &lt;a href="https://aws.amazon.com/event-driven-architecture/"&gt;mind-boggling number of events&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now the customer wins. Changing, replacing, or radically rewriting a single, small module and testing and deploying it in the blink of an eye has become a reality. Experimenting — seeing if an idea for a new &lt;a href="https://jtbd.info/2-what-is-jobs-to-be-done-jtbd-796b82081cca"&gt;job to be done&lt;/a&gt; holds up in production — has become cheap as chips using the pay-as-you-go managed services as provided by cloud platforms like AWS, Azure, and GCP.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sTru2gDv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sd899lwk64rbmo1g0mzh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sTru2gDv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sd899lwk64rbmo1g0mzh.png" alt="A field of male relay racers running" width="800" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Leadership: get out of the way of innovation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Low performance: cloud naive leadership
&lt;/h3&gt;

&lt;p&gt;During less volatile times, markets were predictable, transformations were carefully planned and budgeted, and users were okay with incremental changes every so often. Software, and the hardware it ran on, were complex and expensive beasts, mastered only by tech masochists stuffed away in cost centers called IT departments. Nothing got built until it was very meticulously specified in requirements documents. Even then, the delivered result was often wildly different from what the market specialists and innovators defined months or years before, if not already outdated.&lt;/p&gt;

&lt;p&gt;Once more: slow change, little cooperation, suffering users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Elite performance: cloud-native leadership
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.wsj.com/articles/SB10001424053111903480904576512250915629460"&gt;Software has been eating the world&lt;/a&gt; for a while now. Organizations need to adapt or accept a slow and painful decline into irrelevance. Users now expect to get what they need before realizing they do: a new Netflix series, a recommended product, or even &lt;a href="https://www.tesla.com/support/software-updates"&gt;an improved driving experience&lt;/a&gt;. Understanding users is key to market success and requires continuous experimentation and software development mastery. The cloud is a massive enabler: it enables organizations to focus on user needs by handing the management of &lt;a href="https://medium.com/wardleymaps/on-being-lost-2ef5f05eb1ec"&gt;undifferentiated&lt;/a&gt; technology over to the cloud provider.&lt;/p&gt;

&lt;p&gt;But while technical &lt;a href="https://aws.amazon.com/blogs/enterprise-strategy/training-the-entire-enterprise-to-harness-the-clouds-full-value/"&gt;skill development is essential&lt;/a&gt;, it is just one part of the innovation puzzle. Another is &lt;a href="https://medium.com/aws-enterprise-collection/culture-eats-strategy-for-breakfast-a9c2e091a070"&gt;culture&lt;/a&gt;: leaders need to focus everyone’s attention on the importance of time-to-value and agility as the metrics for success. To create empowered, self-sufficient, and effective teams, people need to come together and shatter the walls previously separating them. Lastly, leaders need to take the long view and realize that short-term risk aversion can be a major innovation blocker.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vAKzrB9q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v2hz31qwdlxs79dgd6hq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vAKzrB9q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v2hz31qwdlxs79dgd6hq.png" alt="Two female relay racers running" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  To boldly go, together
&lt;/h2&gt;

&lt;p&gt;Transforming an organization from cloud naiveness into cloud-native isn’t for the faint of heart, but very necessary nonetheless. Luckily, Luminis has traveled this path many times before and is a very effective guide on the path to cloud-native. For example, we helped OHRA, one of the largest Dutch digital insurers, &lt;a href="https://www.luminis.eu/cases/ohra-cloud-migration/"&gt;migrate its complete data center to the cloud&lt;/a&gt;. Another example is the &lt;a href="https://www.luminis.eu/cases/the-learning-network-innovations-for-digital-education/"&gt;digital transformation of The Learning Network (TLN)&lt;/a&gt; we helped shape and implement.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.luminis.eu/contact/"&gt;Get in touch&lt;/a&gt; and let us help you kickstart your cloud-native transformation.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cloud</category>
      <category>aws</category>
    </item>
    <item>
      <title>What is resilience engineering? A lightning talk with background information</title>
      <dc:creator>Piet van Dongen</dc:creator>
      <pubDate>Sat, 15 May 2021 13:00:30 +0000</pubDate>
      <link>https://dev.to/pietvandongen/what-is-resilience-engineering-a-lightning-talk-with-background-information-3n27</link>
      <guid>https://dev.to/pietvandongen/what-is-resilience-engineering-a-lightning-talk-with-background-information-3n27</guid>
      <description>&lt;p&gt;&lt;strong&gt;The coronavirus pandemic has taught us two things: all online systems will fail eventually, and they will fail in unpredictable ways. But have no fear; resilience engineering is here!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Building and maintaining event-driven, highly scalable, and quickly adaptable systems are a pain in the neck. Resilience engineering embraces this complexity and helps you manage it.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://teqnation.com/" rel="noopener noreferrer"&gt;TEQnation&lt;/a&gt; lightning talk below, I will explain what resilience engineering is, why every software person should know about it, and how you can start practicing it. You can find the transcript below.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/7jB2k16ucQM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  More on resilience engineering
&lt;/h2&gt;

&lt;p&gt;Hungry for more? Great! This might help:&lt;/p&gt;

&lt;h3&gt;
  
  
  Resilience engineering: where do I start?
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://www.resilience-engineering-association.org/about-rea/" rel="noopener noreferrer"&gt;Resilience Engineering Association (REA)&lt;/a&gt; has created a &lt;a href="https://www.resilience-engineering-association.org/resources/where-do-i-start/" rel="noopener noreferrer"&gt;lovely introductory guide on resilience engineering&lt;/a&gt;. After reading or scanning it, you should have a pretty good idea about what resilience engineering is, where it came from and who to follow if you want to learn more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Safety science
&lt;/h3&gt;

&lt;p&gt;Resilience engineering takes a lot of inspiration from &lt;a href="https://www.journals.elsevier.com/safety-science" rel="noopener noreferrer"&gt;safety science&lt;/a&gt;. In short, safety science aims to help people die less and in less horrible ways. A prominent researcher is &lt;a href="https://erikhollnagel.com/ideas/safety-i-and-safety-ii-2014" rel="noopener noreferrer"&gt;Erik Hollnagel&lt;/a&gt;, who introduced the terms &lt;a href="https://www.skybrary.aero/bookshelf/books/2437.pdf" rel="noopener noreferrer"&gt;&lt;strong&gt;Safety I&lt;/strong&gt; and &lt;strong&gt;Safety II&lt;/strong&gt;&lt;/a&gt;. The former focuses on preventing error, the latter on increasing success.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4532d5iy0sa0unjs47i4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4532d5iy0sa0unjs47i4.png" alt="Safety I vs. Safety II"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Complex systems
&lt;/h3&gt;

&lt;p&gt;The term &lt;strong&gt;system&lt;/strong&gt; has special meaning in the context of resilience engineering. It is about both &lt;strong&gt;humans&lt;/strong&gt; as well as &lt;strong&gt;technology&lt;/strong&gt;, thus the term &lt;strong&gt;socio-technical system&lt;/strong&gt;. When resilience engineers talk about complex systems and how they fail, they focus on the whole, from broken machine parts to overworked maintenance workers and increasing market pressures.&lt;/p&gt;

&lt;p&gt;Richard Cook has written &lt;a href="https://how.complexsystems.fail/" rel="noopener noreferrer"&gt;a surprisingly readable paper on how complex systems fail&lt;/a&gt;. Another great resource is the book &lt;a href="https://www.goodreads.com/book/show/10258783-drift-into-failure" rel="noopener noreferrer"&gt;Drift into Failure&lt;/a&gt; by Sidney Dekker, which contains examples ranging from economic collapse to airplanes crashing into the sea.&lt;/p&gt;

&lt;p&gt;Here’s Richard Cook again, this time using the human body to explain what resilience engineering is all about:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/8LbePBiOvZ4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Blameless post mortems
&lt;/h3&gt;

&lt;p&gt;An essential element of resilience engineering is the &lt;strong&gt;post mortem&lt;/strong&gt;. To emerge from failure more strongly than before — a critical property of a resilient system — engineers need to learn how to think about finding and fixing &lt;strong&gt;causes&lt;/strong&gt; of failure. That‘s why you can often hear resilience engineering advocates (confusingly) proclaim: &lt;a href="https://www.kitchensoap.com/2012/02/10/each-necessary-but-only-jointly-sufficient/" rel="noopener noreferrer"&gt;there is no root cause&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;John Allspaw can explain really well what is needed to dig into failure and learn from it:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/4nRahQddtJ0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  DevOps
&lt;/h3&gt;

&lt;p&gt;Resilience engineering has an ongoing and major influence on the DevOps movement. Disciplines like &lt;a href="https://en.wikipedia.org/wiki/Chaos_engineering" rel="noopener noreferrer"&gt;chaos engineering&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Site_reliability_engineering" rel="noopener noreferrer"&gt;site reliability engineering&lt;/a&gt; are heavily leaning on resilience engineering principles and research.&lt;/p&gt;

&lt;p&gt;Gene Kim and John Willis extensively talk about the origins of this side of DevOps (and much more) in their audio series &lt;a href="https://itrevolution.com/book/beyond-phoenix-project/" rel="noopener noreferrer"&gt;Beyond the Phoenix Project&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blog series
&lt;/h3&gt;

&lt;p&gt;Learning about resilience engineering is an ongoing effort here at Luminis. That‘s why we decided to start &lt;a href="https://www.luminis.eu/category/blog/resilience-en/" rel="noopener noreferrer"&gt;a blog series on resilience engineering&lt;/a&gt;. We will continue to add to it over the next few years, and I invite you to follow us and give us your feedback. I believe that writing, reading, and reflecting are excellent (and fun!) ways to become experts on complex subjects like resilience engineering.&lt;/p&gt;

&lt;p&gt;Thanks again for your attention!&lt;/p&gt;

&lt;h2&gt;
  
  
  Video transcript
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Welcome
&lt;/h3&gt;

&lt;p&gt;Hi everyone! And welcome to my TEQnation lightning talk titled: What is resilience engineering?&lt;/p&gt;

&lt;h3&gt;
  
  
  About me
&lt;/h3&gt;

&lt;p&gt;Before we dive into the details, I want to talk about my favorite subject: me! I am Piet van Dongen. I am 37 years old. I work at Luminis, a super nice Dutch company that is all about technology leadership, software craftsmanship, and sharing knowledge. Check us out at &lt;a href="http://www.luminis.eu!" rel="noopener noreferrer"&gt;www.luminis.eu!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I once started my career as a primary school teacher, then moved to web design, front end engineering, back end engineering, and now I am a Cloud Consultant.&lt;/p&gt;

&lt;p&gt;I am really, really excited about cloud technology. And I think understanding resilience engineering is essential for anyone working with technology — especially cloud technology, which enables us to build and evolve super-complex systems.&lt;/p&gt;

&lt;p&gt;I’d love to tell you more about me, but this is a lightning talk. Let’s get cracking!&lt;/p&gt;

&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;What will you learn in the next 10 minutes? This is a talk about resilience engineering, so we’ll start off with resilience.&lt;/p&gt;

&lt;p&gt;What is resilience exactly? This will give you an idea of what resilience is and is not.&lt;/p&gt;

&lt;p&gt;Then, the engineer parts comes into play. I’ll talk about safety science, systems thinking and the socio-technical system.&lt;/p&gt;

&lt;p&gt;Finally, I’ll give you some pointers. So you can do a resilience engineering deep dive yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resilience
&lt;/h3&gt;

&lt;p&gt;Now, what is resilience?&lt;/p&gt;

&lt;p&gt;To understand resilience, you need to think about &lt;strong&gt;robustness&lt;/strong&gt; first. We — you know, software people — are really good at building &lt;strong&gt;robust&lt;/strong&gt; systems. Systems that are highly available, that can handle a little peak load, a little stress. To do so, we use techniques like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Redundancy.&lt;/li&gt;
&lt;li&gt;  Fallbacks.&lt;/li&gt;
&lt;li&gt;  Retries.&lt;/li&gt;
&lt;li&gt;  Failovers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sound familiar? Great.&lt;/p&gt;

&lt;p&gt;We do this because we tend to think about what might go &lt;strong&gt;wrong&lt;/strong&gt;. You know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Servers might fail.&lt;/li&gt;
&lt;li&gt;  Networks might become unreliable.&lt;/li&gt;
&lt;li&gt;  Things go brrrrr.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We don’t want that. So we design around all the things we know can go wrong. Resilience engineering people sometimes call this: &lt;strong&gt;the known unknowns&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Robustness is nice, but it does not protect us when things go south in the most unexpected ways. Redundancy won’t help when the Retro Encabulator’s hydrocoptic marzel vanes go from horizontal to vertical fumbling. We call these: &lt;strong&gt;unknown unknowns&lt;/strong&gt;. Stuff &lt;strong&gt;we don’t know we don’t know&lt;/strong&gt;, to quote Ronald Rumsfeld.&lt;/p&gt;

&lt;p&gt;That’s the stuff resilient systems can handle. And robust systems can not.&lt;/p&gt;

&lt;p&gt;Take this glass, for example. I can do this (Piet taps the glass), even this (Piet taps the concrete floor with the glass). It’s designed to withstand all that. It’s robust. But it is not very resilient. If I do this (Piet smashes the glass with a hammer), it breaks.&lt;/p&gt;

&lt;p&gt;But, smashing glasses with a hammer is not a good example of resilience engineering. Smashing with a hammer is not an &lt;strong&gt;unknown unknown&lt;/strong&gt; for glassware designers. It’s just not a requirement.&lt;/p&gt;

&lt;p&gt;Hmm, lemme think…&lt;/p&gt;

&lt;p&gt;Yes, I have a better example!&lt;/p&gt;

&lt;p&gt;(Piet takes a hammer. And smashes fingers. He screams loudly. Fade to black. Elevator music plays. Fade back. He now has bandages on his fingers.)&lt;/p&gt;

&lt;p&gt;Piet, now with a trembling, high-pitched voice:&lt;/p&gt;

&lt;p&gt;Yes, a much better example. The human body is an excellent example of a resilient system. You see, even though I smashed my fingers with a hammer, my body still works. Even better: in a few weeks, these bones will be stronger than before. Great.&lt;/p&gt;

&lt;p&gt;Now that I’ve explained the difference between &lt;strong&gt;robust&lt;/strong&gt; and &lt;strong&gt;resilient&lt;/strong&gt; systems let’s dive into the &lt;strong&gt;engineering&lt;/strong&gt; part of resilience engineering.&lt;/p&gt;

&lt;h3&gt;
  
  
  Safety science
&lt;/h3&gt;

&lt;p&gt;Let’s go back to my example about &lt;strong&gt;known unknowns&lt;/strong&gt;. The things we &lt;strong&gt;know&lt;/strong&gt; can go wrong. If you think about things that can go &lt;strong&gt;wrong&lt;/strong&gt; a lot, you tend to focus on &lt;strong&gt;preventing error&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And how do we prevent error?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  By &lt;strong&gt;minimizing variance&lt;/strong&gt;. For example, by not allowing certain input.&lt;/li&gt;
&lt;li&gt;  By &lt;strong&gt;writing&lt;/strong&gt; a LOT of &lt;strong&gt;documentation&lt;/strong&gt;. The Boeing 747 flight crew operating manual is 3,000 pages long.&lt;/li&gt;
&lt;li&gt;  By &lt;strong&gt;enforcing&lt;/strong&gt; things. Like making it impossible to shift to reverse while driving your car.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s all fine and dandy, but it only helps us prevent the &lt;strong&gt;known unknowns&lt;/strong&gt;. And there are a LOT more &lt;strong&gt;un&lt;/strong&gt;known unknowns.&lt;/p&gt;

&lt;p&gt;Have you seen the HBO series Chernobyl? A great example of disaster caused by unknown unknowns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Graphite tips.&lt;/li&gt;
&lt;li&gt;  Bad management.&lt;/li&gt;
&lt;li&gt;  Cover up by the government.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just to name a few. So how do you prevent that? That’s what the field of &lt;strong&gt;safety science&lt;/strong&gt; is all about, the birthplace of resilience engineering.&lt;/p&gt;

&lt;p&gt;In trying to create a safer world, safety scientists are trying to shift the focus from avoiding things that go &lt;strong&gt;wrong, sometimes&lt;/strong&gt;, to making sure things go &lt;strong&gt;right, most of the time&lt;/strong&gt;. They do this by focusing not only on &lt;strong&gt;technology&lt;/strong&gt; but on &lt;strong&gt;humans&lt;/strong&gt; as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Socio-technical systems
&lt;/h3&gt;

&lt;p&gt;When resilience engineering advocates talk about systems, they do not mean components or computers, or networks. They are talking about the so-called &lt;strong&gt;socio-technical&lt;/strong&gt; system. Socio as in: the people that interact with the technology. This perspective is known as &lt;strong&gt;systems thinking&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You see, whether you like it or not, humans are always part of the systems we create. Take the Chernobyl example again. What was ‘the system’ there? Just the nuclear power plant? The control room, the reactor, the steam turbines. Or the people as well? The operators, the managers, the government.&lt;/p&gt;

&lt;p&gt;When thinking in socio-technical systems, the humans are just as important as the machines.&lt;/p&gt;

&lt;p&gt;So what? How does this prevent error? What’s the difference? How do you engineer resilience, then? Surely, you can only create systems, not the humans as well? That’s where the engineering part of resilience engineering finally comes into play.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resilience engineering
&lt;/h3&gt;

&lt;p&gt;When things go wrong, they do not go wrong suddenly and all at once. Again, Chernobyl. The explosion of the reactor core was the climax, for sure. But what caused it?&lt;/p&gt;

&lt;p&gt;The operators in the control room pushing a shutdown button? Maybe, but they did not anticipate the disastrous outcome. They didn’t even completely understand what was happening at the time, even though they were surrounded by tons of data. Also, they were under a lot of pressure from their superiors. Who were under pressure themselves as well. Plus, nobody imagined that a shutdown would cause an explosion. What happened was radically different from what people &lt;strong&gt;imagined&lt;/strong&gt; would happen.&lt;/p&gt;

&lt;p&gt;As you see, a lot of factors are at play here. Both &lt;strong&gt;human&lt;/strong&gt; factors as well as &lt;strong&gt;technical&lt;/strong&gt; factors. Socio. Technical. And I think that is the central theme of resilience engineering: to make sure &lt;strong&gt;things go right, most of the time&lt;/strong&gt;, using the engineer skills we have.&lt;/p&gt;

&lt;p&gt;How do we do that? By helping the human part of our systems as much as possible.&lt;/p&gt;

&lt;p&gt;One way to do that is by creating transparent systems. Systems that can tell you what is happening, what state it is in at any point in time. Systems with clear controls, interfaces that enable operators to control the system when they decide they need to. That way, we enable humans to imagine systems as close as possible as to their actual state, which is, of course, impossible.&lt;/p&gt;

&lt;p&gt;Another way to practice resilience engineering is by creating easily extensible systems. Systems that can be improved over time. So write clear, modular code. Implement architectures that can evolve. Make sure you are clear about what your code does, or at least: should and should not do.&lt;/p&gt;

&lt;p&gt;Lastly, when things go wrong, and they &lt;strong&gt;will&lt;/strong&gt; go wrong, keep an open mind. Don’t focus on a single root cause because it does not exist, and it only limits your chances of success. Don’t blame each other, but use the opportunity to create an even better system. A system that increases the chances of success over time.&lt;/p&gt;

&lt;p&gt;That is what resilience engineering is all about.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;So in conclusion:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Resilience is not robustness.&lt;/li&gt;
&lt;li&gt;  Resilience engineering is about creating success, not eliminating error.&lt;/li&gt;
&lt;li&gt;  Resilience engineering is about humans &lt;strong&gt;and&lt;/strong&gt; technology.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope you have learned something today. This talk only scratches the surface, of course. And the same goes for me: the more I learn about resilience engineering, the more I realize there is much more to learn. That’s why I’ve created a blog post that will help you dive deeper. You can find it here.&lt;/p&gt;

&lt;p&gt;Thank you very much for listening to me. I hope you have a great day!&lt;/p&gt;

</description>
      <category>resilience</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>The new normal demands resilience engineering</title>
      <dc:creator>Piet van Dongen</dc:creator>
      <pubDate>Tue, 12 Jan 2021 21:40:16 +0000</pubDate>
      <link>https://dev.to/pietvandongen/the-new-normal-demands-resilience-engineering-4a4j</link>
      <guid>https://dev.to/pietvandongen/the-new-normal-demands-resilience-engineering-4a4j</guid>
      <description>&lt;p&gt;&lt;strong&gt;Understanding resilience engineering is essential for organizations that want to survive as we accelerate into the online future. Let me tell you what resilience engineering is and how it can help you roll with the punches that come with success.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0SONDjVn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/o4qv99yx5gub0vgq9e1p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0SONDjVn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/o4qv99yx5gub0vgq9e1p.png" alt="No school today 😕" width="260" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The only way to keep the Dutch at home is by &lt;a href="https://www.theguardian.com/world/2020/dec/14/italy-likely-to-follow-germany-with-covid-christmas-lockdown"&gt;grounding their offspring&lt;/a&gt;, as our government has discovered twice over. So, after spending two weeks dreading the end of the holiday season, last week marked the start of the second homeschooling season in a year.&lt;/p&gt;

&lt;p&gt;Thanks mostly to my very structured wife and non-male children (&lt;a href="https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3617953"&gt;no coincidence there&lt;/a&gt;), our household was prepared. We set up two desks in the living room, made schedules, and brought laptops, tablets, headphones, pencils, paper. But when it was time for the digital school gates to open, they… &lt;a href="https://twitter.com/JCdeGraaf/status/1346004962782961664"&gt;didn’t&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watskeburt?
&lt;/h2&gt;

&lt;p&gt;In our case, the school’s &lt;a href="https://zuluconnect.net/"&gt;digital learning&lt;/a&gt; and &lt;a href="https://www.parnassys.nl/producten/modules/parro"&gt;communication&lt;/a&gt; platforms choked on the peak load all the homeschoolers generated. Luckily, Netflix/YouTube/the bookcase/outside was still online, so we just postponed the inevitable for a bit. By lunchtime, everything was already forgotten, replaced by the regular remote schooling joys and frustrations.&lt;/p&gt;

&lt;p&gt;After dinner and coffee and ice cream, and some delicious white chocolate, I, ehm… Where was I? Oh yeah, I started to get curious about that morning’s outage. And I found an interesting &lt;a href="https://www.parnassys.nl/parro-status"&gt;ongoing status report by the Parro team&lt;/a&gt;. Apparently, they already anticipated the surge and had taken what they believed would be enough measures to survive it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“To be fair, we expected that Parro, with all the measures taken, would be able to handle the new extreme peak traffic.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After what I imagine must have been a stressful few hours, the Parro team fixed all issues and had a smoothly running system by the end of the day again. Kudos to them. That’s what mattered most to their customers, the teachers and parents of our small kingdom.&lt;/p&gt;

&lt;p&gt;But their work is not done, as the updates on the status page show. Where is the next bottleneck? When will it become a problem? How will they fix it? Will they ever be done?&lt;/p&gt;

&lt;p&gt;They won’t. This is the new normal. But it doesn’t have to be a negative thing. There is a way to use these weird times to engineer ourselves into the future.&lt;/p&gt;

&lt;p&gt;Let‘s examine how.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HbAiOo__--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/dn3eax8od8s4mfmxmu8q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HbAiOo__--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/dn3eax8od8s4mfmxmu8q.png" alt="" width="800" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Peaks are the new normal
&lt;/h2&gt;

&lt;p&gt;Now that we are &lt;a href="https://www.thinkwithgoogle.com/intl/en-154/future-of-marketing/digital-transformation/covid-accelerated-digital-adoption/"&gt;accelerating into the online future&lt;/a&gt;, unpredictable massive peak usage will become just another truth. Scary? Sure. Exciting? I think so. But a reality, nonetheless.&lt;/p&gt;

&lt;p&gt;This will lead to two types of problems to deal with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Known unknowns&lt;/strong&gt;: a problem that hasn’t happened yet, but is not surprising once it happens. Server failures, network troubles, that kind of stuff.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Unknown unknowns&lt;/strong&gt;: something that comes as a complete surprise, shocking systems into failure. Or, &lt;a href="https://en.wikipedia.org/wiki/There_are_known_knowns"&gt;as Donald Rumsfeld called them&lt;/a&gt;: “the ones we don’t know we don’t know.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first type is tough enough to handle as it is. But a well designed, &lt;a href="https://www.resilience-engineering-association.org/resources/where-do-i-start/#what-is-resilience"&gt;robust&lt;/a&gt; system can withstand these known unknowns. Redundancy, retries, fallbacks, failovers: when designing highly available systems, these terms are familiar. We use them to deflect what we know can go wrong, like a server going down or a request to a service timing out.&lt;/p&gt;

&lt;p&gt;But what about a global pandemic leading to sudden, massive demand on a system designed for a different reality? Surely, you can’t handle all the ways a system might break down under these unpredictable conditions?&lt;/p&gt;

&lt;p&gt;But you can. It’s called &lt;strong&gt;resilience engineering&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling the unknown unknowns
&lt;/h2&gt;

&lt;p&gt;Resilience engineering is a field still very much inside the scientific domain. It is closely related to the areas of human error, cognitive systems engineering, and safety. You know, &lt;a href="https://www.goodreads.com/book/show/10258783-drift-into-failure"&gt;plane crashes, oil spills, and nuclear disasters&lt;/a&gt;, that type of stuff.&lt;/p&gt;

&lt;p&gt;Websites and mobile apps going down rarely lead to environmental disaster or death. That doesn’t mean we can’t learn anything from the tons of research that went into making our world a little safer. Resilience engineering takes all the good stuff that prevents 747s and powerplants from going down and applies it to our young (software) engineering field.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wR7c-jax--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/6swn1n41a9bxhfgu9k08.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wR7c-jax--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/6swn1n41a9bxhfgu9k08.png" alt="" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Old thinking, new thinking
&lt;/h2&gt;

&lt;p&gt;Like my colleague Hans Bossenbroek so eloquently put: &lt;a href="https://www.luminis.eu/blog/resilience-en/resilience-the-rise-of-events-a-copernican-view-on-data/"&gt;as the need for agility and speed increases, we need to move towards event thinking&lt;/a&gt;. The solution: event-driven, modular, highly scalable systems. But also: &lt;a href="https://twitter.com/Werner/status/741673514567143424"&gt;super complex architectures resembling chaotic spider webs&lt;/a&gt;. And a gazillion events flowing through at breakneck speed.&lt;/p&gt;

&lt;p&gt;Of course, that’s not something you design and deploy at the end of the first sprint. And it might be a long way off from your massive monolith. But since we live in web-scale-or-die-times, it’s the logical solution to real-world problems. As the technology evolves, so should the way you manage it. You have to fight the urge to control the new system the way you did your old one.&lt;/p&gt;

&lt;p&gt;Instead of focusing on the things that might go &lt;strong&gt;wrong&lt;/strong&gt; (known unknowns!), try instead to &lt;a href="https://www.england.nhs.uk/signuptosafety/wp-content/uploads/sites/16/2015/10/safety-1-safety-2-whte-papr.pdf"&gt;look at the things that go &lt;strong&gt;right&lt;/strong&gt; and do those things more often&lt;/a&gt;. This will help you and your teams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Understand what leads to the &lt;strong&gt;right&lt;/strong&gt; thing and do that &lt;strong&gt;more&lt;/strong&gt; often.&lt;/li&gt;
&lt;li&gt;  Increase the chance of &lt;strong&gt;success&lt;/strong&gt; instead of nervously awaiting the next breakdown.&lt;/li&gt;
&lt;li&gt;  Be &lt;strong&gt;proactive&lt;/strong&gt; instead of reactive.&lt;/li&gt;
&lt;li&gt;  Drive towards &lt;strong&gt;continuous improvement&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ve read that right; it’s about humans as much as it is about hardware and software!&lt;/p&gt;

&lt;h2&gt;
  
  
  Systems thinking
&lt;/h2&gt;

&lt;p&gt;You see, systems encompass both people and technology. Think about it: when was the last time you heard about a developer accidentally deleting a production database? Or the other way around: some Sherlock Holmes type saving the day by tweaking a setting even the most experienced colleague was unaware of?&lt;/p&gt;

&lt;p&gt;And that is where resilience comes from: humans doing the right things. We do that because we just know what is right. We have a knack for it, some kind of intuition. It’s what keeps complex systems from falling apart.&lt;/p&gt;

&lt;p&gt;And when it does fall apart, like last week, something must have tipped the scales. Despite all the hard work by the humans and the computers, systems go down from time to time. And when they do, they have to be brought back up as soon as possible.&lt;/p&gt;

&lt;p&gt;Now, stop and think: if production goes down, what do you do? Maybe something like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Find the root cause.&lt;/li&gt;
&lt;li&gt;  Fix it.&lt;/li&gt;
&lt;li&gt;  Make sure that never ever happens again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to practice resilience engineering: wrong answer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Csa45I3---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/r5z98q66hlrm2kbsuxao.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Csa45I3---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/r5z98q66hlrm2kbsuxao.png" alt="" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  There is no root cause!
&lt;/h2&gt;

&lt;p&gt;Stop again. Think about the complex system. We already saw that keeping a system running is a balancing act. Finding a root cause would imply that there is a &lt;strong&gt;single thing&lt;/strong&gt; that caused the error. If good behavior is explained by complexity, how can a single cause explain faulty behavior?&lt;/p&gt;

&lt;p&gt;It can’t. It‘s a logical fallacy. &lt;a href="https://www.kitchensoap.com/2012/02/10/each-necessary-but-only-jointly-sufficient/"&gt;There is no root cause&lt;/a&gt;.&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--4-6ISSbO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://pbs.twimg.com/profile_images/968616101092339712/nZvXwW7w_normal.jpg" alt="John Allspaw profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        John Allspaw
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @allspaw
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kDgU_xDI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      There is no root cause. &lt;a href="http://t.co/94CHGcek"&gt;bit.ly/wIvsrQ&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/devops"&gt;#devops&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      16:01 PM - 10 Feb 2012
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=168001737628721154" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OXOJJiQT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=168001737628721154" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--foTp-unf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=168001737628721154" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SFHqU4bF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;This also means that a proper postmortem should not be about explaining the hunt for a single point of failure. An excellent postmortem is about showing that you understand that complexity sometimes results in unwanted behavior. And that you realize, in hindsight, that many things were working together until they didn’t. But that you have all learned from it. That you now know how to do even more things right.&lt;/p&gt;

&lt;p&gt;In other words: good postmortems are &lt;a href="https://codeascraft.com/2012/05/22/blameless-postmortems/"&gt;blameless&lt;/a&gt;, balancing team safety and accountability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going resiliently into the future
&lt;/h2&gt;

&lt;p&gt;So, to err is… complex. Faults are a given. Resilience engineering is not about preventing them. It‘s about doing ever more things right so that you can increase complexity while maintaining control. So that when the next unknown unknown strikes, you will be prepared.&lt;/p&gt;

&lt;p&gt;So get to it! Learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  What is resilience engineering? (You already know!)&lt;/li&gt;
&lt;li&gt;  How do I start practicing it?&lt;/li&gt;
&lt;li&gt;  Whom should I involve?&lt;/li&gt;
&lt;li&gt;  How do we get better over time?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Need help? Keep an eye on &lt;a href="https://www.luminis.eu/category/blog/resilience-en/"&gt;our blog series on resilience engineering&lt;/a&gt; to find out the answers to these questions!&lt;/p&gt;

&lt;h2&gt;
  
  
  Observations on the resilience of bone
&lt;/h2&gt;

&lt;p&gt;In the meantime, here is an excellent talk by Dr. Richard Cook about resilience engineering and bones. Bones? Yes, you know, you have them inside your body most of the time! And they are a great vehicle for explaining two types of resilience engineering. Watch and learn:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/8LbePBiOvZ4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>resilience</category>
      <category>devops</category>
      <category>performance</category>
    </item>
    <item>
      <title>Migrating to the cloud: 6 strategies for future success</title>
      <dc:creator>Piet van Dongen</dc:creator>
      <pubDate>Wed, 16 Dec 2020 09:59:44 +0000</pubDate>
      <link>https://dev.to/pietvandongen/migrating-to-the-cloud-6-strategies-for-future-success-5c0c</link>
      <guid>https://dev.to/pietvandongen/migrating-to-the-cloud-6-strategies-for-future-success-5c0c</guid>
      <description>&lt;p&gt;Let’s say you’re convinced (and rightly so!) that the future of your organization is in the cloud. How will you get there? Many roads lead to Rome, some more complex and promising than others. In this blog, I will share six strategies for getting into the cloud successfully. Which one fits your organization best?&lt;/p&gt;

&lt;p&gt;Choosing a migration strategy strongly depends on the goals you wish to achieve. Some approaches offer you many strategic opportunities, but they are often a bit more complex to implement. Some options are relatively straightforward but come with higher costs in the long run. Therefore, be aware of the &lt;em&gt;why&lt;/em&gt; of your migration and choose the strategy that best suits your needs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dl3v8VPu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/uq0zc6h1gt2eewid9494.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dl3v8VPu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/uq0zc6h1gt2eewid9494.png" alt="Cloud migration strategies" width="800" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy 1: Lift &amp;amp; Shift
&lt;/h2&gt;

&lt;p&gt;Also called &lt;a href="https://azure.microsoft.com/en-us/migration/migration-journey/#migrate"&gt;rehosting&lt;/a&gt;, the idea is simple: you move your systems to the cloud platform with as little change as possible. You basically move your whole data center to the cloud.&lt;/p&gt;

&lt;p&gt;Rehosting may be an attractive choice: you’re done relatively quickly, and little risk is involved. However, this strategy has a significant disadvantage: &lt;a href="https://www.ibm.com/cloud/learn/lift-and-shift#toc-lift-and-s-hwOAr_AK"&gt;operational costs will be relatively high&lt;/a&gt;, especially in the long run. Furthermore, it will take some time to set up your new environment and connect your existing CI/CD solutions.&lt;/p&gt;

&lt;p&gt;Do you need to move out of your data center sooner rather than later? This strategy might be a good fit. But beware: &lt;a href="https://acloudguru.com/blog/engineering/the-lift-and-shift-shot-clock-cloud-migration"&gt;the clock is ticking&lt;/a&gt;. Once the migration is done, you need to start changing your applications and architecture for a better cloud fit. Monitor your costs meticulously, so you know which applications are good candidates for the next round of refactoring.&lt;/p&gt;

&lt;p&gt;Another option is to do targeted lifts and shifts. There’s a good chance that a handful of older systems has to move to the cloud as well. You can move them &lt;a href="https://www.cnpatterns.org/infrastructure-cloud/lift-and-shift-at-the-end"&gt;at the end of the migration&lt;/a&gt;, then phase them out or replace them at a later stage.&lt;/p&gt;

&lt;p&gt;In short: this can be a useful strategy, but use it wisely and sparingly. Lifting and shifting is a way to quickly get into the cloud, but it comes at the cost of having to do more work afterward.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy 2: Replatforming
&lt;/h2&gt;

&lt;p&gt;A.k.a. &lt;a href="https://aws.amazon.com/blogs/enterprise-strategy/6-strategies-for-migrating-applications-to-the-cloud/"&gt;lift-tinker-and-shift&lt;/a&gt;. You’re still not changing your systems’ functionalities, but the underlying platform gets an upgrade.&lt;/p&gt;

&lt;p&gt;Do you have a chunky database server with an expensive license running somewhere? Then this is a great option. A &lt;a href="https://azure.microsoft.com/nl-nl/product-categories/databases/"&gt;database-as-a-service&lt;/a&gt; is a considerable improvement in several ways: no more managing systems and paying for idle time.&lt;/p&gt;

&lt;p&gt;Another example: a colossal application server running lots of deployments on expensive hardware. Setting up and maintaining clusters of those is very time consuming and complex. &lt;a href="https://www.oreilly.com/library/view/enterprise-docker/9781491994986/ch04.html"&gt;Moving these applications to Docker containers&lt;/a&gt; is certainly an attractive option in that perspective.&lt;/p&gt;

&lt;p&gt;Beware the disadvantages, though, which mostly lie in the less visible parts. The details of the cloud’s underlying platform differ subtly from your on-prem’s. Keep the &lt;a href="https://www.youtube.com/watch?v=z6S_gAqD8nw"&gt;fallacies of distributed computing&lt;/a&gt; in mind and practice &lt;a href="https://cloud.google.com/solutions/scalable-and-resilient-apps"&gt;resiliency&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Not only does this strategy provide you with the chance to &lt;a href="https://www.oreilly.com/library/view/google-cloud-platform/9781788834308/291dbaba-2bb3-418d-86bf-4e9c835f24a2.xhtml"&gt;shift your IT spending to a radically different model&lt;/a&gt;, but it can also greatly improve your organization’s agility. So: lots of cloud gold, without touching your architecture. But there’s more to gain when you move towards cloud-native.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy 3: Refactoring / rearchitecting
&lt;/h2&gt;

&lt;p&gt;Refactoring (also called: rearchitecting) is the most far-reaching strategy in terms of architectural and applicative change, but it’s &lt;a href="https://aws.amazon.com/executive-insights/content/generating-business-value-with-aws-serverless/"&gt;bursting with potential&lt;/a&gt;. By most effectively using what the cloud offers, things that were impossible on-prem are within reach when taking the rearchitecting route. Think unprecedented scalability for minimal costs and using &lt;a href="https://azure.microsoft.com/en-us/services/#ai-machine-learning"&gt;services that would conventionally mean huge upfront material and human capital investments&lt;/a&gt;. Not to mention time-to-value improvements.&lt;/p&gt;

&lt;p&gt;The impact of this strategy is mostly dependent on the current state of your system landscape. How tightly coupled are your applications? How modular is your architecture? If you’re already reasonably service-oriented, you’re halfway there. A step towards &lt;a href="https://www.youtube.com/watch?v=wgdBVIX9ifA"&gt;microservices&lt;/a&gt; — even better: &lt;a href="https://www.youtube.com/watch?v=x0n26bKT-eA"&gt;serverless&lt;/a&gt; — is not a giant leap from there. It‘ll put you right at the cutting edge.&lt;/p&gt;

&lt;p&gt;Fortunately, again, this isn’t an all-or-nothing strategy. It’s an excellent fit for migrating applications that exhibit cloud-native characteristics. But it is equally interesting to view rearchitecting from a business value perspective.&lt;/p&gt;

&lt;p&gt;Suppose you could do magic and instantly accelerate your idea-to-production time. Deliver multiple times a day. With fewer bugs. Without having to compromise on security and stability. Which applications would yield the best results then? That part of your portfolio probably benefits the most from the refactoring / rearchitecting strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy 4: Keep
&lt;/h2&gt;

&lt;p&gt;Rest assured, migrations are not all about doing buzzword bingo heavy work. Not all systems have to be migrated. Some are doing just fine in the data center. You might migrate them later on, or maybe you can switch them off entirely in the future.&lt;/p&gt;

&lt;p&gt;During a cloud migration, you will acquire tremendous amounts of technical know-how. But a cloud migration is not a strictly technical affair. Slowly but surely, your organization will start shifting towards a new way of thinking and working (together). Sometimes, systems turn out to be not such a good fit anymore. Sure, they can last for a little while longer, but they need to go sooner or later. Bringing them to the cloud might not be worth the hassle.&lt;/p&gt;

&lt;p&gt;Maybe you’ve just gone through a big systems upgrade project. Or have other reasons just to leave some parts be. It might be a valid option.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy 5: Phase out
&lt;/h2&gt;

&lt;p&gt;It’s not always clear from the get-go, or maybe you just didn’t expect it. But during a migration, you always bump into stuff that can be retired.&lt;/p&gt;

&lt;p&gt;For example, when we helped &lt;a href="https://www.ohra.nl/"&gt;OHRA&lt;/a&gt; plan and execute their migration from their data center to the AWS cloud, they eventually switched off about 20% of their applications. That saved them a lot of migration work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategy 6: Replace (with SaaS)
&lt;/h2&gt;

&lt;p&gt;This one is aimed at systems that cost a lot but yield little. Replacing them with SaaS solutions is a viable strategy here. A cluster full of mail servers and file servers filled with spreadsheets might be a candidate for replacement with Office 365 or Google Workspace. Salesforce can replace a CRM system, and an on-prem content management system has plenty of SaaS alternatives.&lt;/p&gt;

&lt;p&gt;Developing software is costly enough as it is. Save time, energy, and money to build something that sets you apart from the competition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which cloud migration strategy is right for your organization?
&lt;/h2&gt;

&lt;p&gt;There is no such thing as a one-size-fits-all cloud migration strategy. Every organization has a different set of goals and ambitions. Pick a strategy that best fits yours. But keep this in mind: you will reap the most cloud benefits if you try to be as cloud-native as possible. The more you stick to the old data center centric model, the more you will see this reflected in your operational costs.&lt;/p&gt;

&lt;p&gt;With the right combination of knowledge, experience, and crew, you can make your cloud migration a success story. We’d be happy to help!&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to know more? Download our cloud migration white paper
&lt;/h2&gt;

&lt;p&gt;My colleague Bert Ertman and I have written a &lt;a href="https://www.luminis.eu/en/whitepapers/whitepaper-cloud-migration/?utm_source=dev-to&amp;amp;utm_medium=blog&amp;amp;utm_campaign=whitepaper-cloud-migration&amp;amp;utm_term=piet-dev-to"&gt;white paper about cloud migrations&lt;/a&gt;. In it, we answer the following questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What &lt;strong&gt;promises&lt;/strong&gt; does the cloud hold? What are its &lt;strong&gt;pitfalls&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;Which migration*&lt;em&gt;strategies&lt;/em&gt;* can you use? (You already know)&lt;/li&gt;
&lt;li&gt;Which &lt;strong&gt;people&lt;/strong&gt; do you need and when?&lt;/li&gt;
&lt;li&gt;How do you &lt;strong&gt;plan&lt;/strong&gt; a cloud migration?&lt;/li&gt;
&lt;li&gt;When can you &lt;strong&gt;start&lt;/strong&gt; with the first migrations?&lt;/li&gt;
&lt;li&gt;How do you gain migration &lt;strong&gt;velocity&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;When and how can you &lt;strong&gt;complete&lt;/strong&gt; a migration successfully?&lt;/li&gt;
&lt;li&gt;What’s &lt;strong&gt;next&lt;/strong&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can download the &lt;a href="https://www.luminis.eu/en/whitepapers/whitepaper-cloud-migration/?utm_source=dev-to&amp;amp;utm_medium=blog&amp;amp;utm_campaign=whitepaper-cloud-migration&amp;amp;utm_term=piet-dev-to"&gt;cloud migration white paper&lt;/a&gt; for free. Have fun reading!&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>migration</category>
      <category>aws</category>
      <category>azure</category>
    </item>
    <item>
      <title>Soundbyte 390: Crystalline</title>
      <dc:creator>Piet van Dongen</dc:creator>
      <pubDate>Sun, 14 Apr 2019 20:44:19 +0000</pubDate>
      <link>https://dev.to/pietvandongen/soundbyte-390-crystalline-2dfk</link>
      <guid>https://dev.to/pietvandongen/soundbyte-390-crystalline-2dfk</guid>
      <description>&lt;p&gt;Whenever something that I have been looking forward to is over, I feel so sad and empty inside. The bigger the event, the worse the feeling. I can vividly remember falling into the black hole after my first birthday-disco-party was over and my last friend left. I just wanted it to not be over, I wanted the fun and freedom to last forever and ever.&lt;/p&gt;

&lt;p&gt;This week the feeling hit me again, twice. The last few weeks I had been rehearsing a play for my daughters' school camp and last Thursday we performed it. It was so much fun to get together with the other parents, to anticipate playing the scenes and to finally see the looks on all the little faces when doing the play. But the sadness afterwards, &lt;em&gt;aaaargh&lt;/em&gt;, I could really have done without it. &lt;/p&gt;

&lt;p&gt;Still, the ups more than make up for the downs, I wouldn't have missed it for the world. Quite literally, because that Thursday also marked our &lt;a href="https://devcon.luminis.eu/"&gt;5th Luminis DevCon&lt;/a&gt;. Luckily I had my phone with me so I could ask my friends what I was missing out on. A lot of good stuff, so they have told me. &lt;/p&gt;

&lt;p&gt;I was especially proud of my colleague &lt;a href="https://devcon.luminis.eu/events/de-nederlandse-rap-generator/"&gt;Sam van Geijn&lt;/a&gt;, who spoke about his super funny &lt;a href="https://nederlandserap.cloud/raps/create"&gt;Dutch Rap Generator&lt;/a&gt;. Sam was still a student last year and now he has done a talk in front of an actual live audience with actual human beings in it. And a very good talk at that, a few friends of mine let me know afterwards. I could write a whole post about what I love about Sam and his talk, which I might actually do later.&lt;/p&gt;

&lt;p&gt;The same goes for the DevCon itself. I love that we have a yearly event about the Art of Software Engineering (not just programming!), and that we do almost everything ourselves. From writing papers to creating &lt;a href="https://devcon.luminis.eu/program/"&gt;a high-quality program&lt;/a&gt;, from the brainstormins to the organisation of the day itself, it's all done by the colleagues we work with every day. And it shows, the feedback from visitors has always been very positive. This I could go on and on about as well, but I won't.&lt;/p&gt;

&lt;p&gt;I want to get back to that feeling of sadness after big events I started this post off with. You see, my personal news bubble this week was filled with the same image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FS5YWAnG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/j0r20dkw2baxgzzze3ap.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FS5YWAnG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/j0r20dkw2baxgzzze3ap.png" alt="First Image of a Black Hole" width="800" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was watching the &lt;a href="https://en.wikipedia.org/wiki/Event_Horizon_Telescope"&gt;Event Horizon Telescope&lt;/a&gt; press conference and when they finally showed the image the whole world seemed to have been waiting for years for, I got that feeling again. That was it, I thought, these people have poured their hearts and souls into this project and now it is over, done, finished. What in the world must they feel like now? Somehow that thought stayed with me the whole week.&lt;/p&gt;

&lt;p&gt;And then this morning, I was a watching a two year old talk by &lt;a href="https://www.theguardian.com/science/2019/apr/11/katie-bouman-black-hole-photo"&gt;Katie Bouman&lt;/a&gt;, explaining how software was going to help her team  capture the image of the black hole:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/BIvezCVcsYs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Watching that made me so happy for some reason. This is so awesome, I thought. These people dreamed of creating a telescope the size of the earth so they could take a picture of something Einstein dreamed up more than a hundred years ago. And then they actually went ahead and did it! How can you be sad about &lt;em&gt;that&lt;/em&gt;? That's awesome! &lt;/p&gt;

&lt;p&gt;I get the same feeling from my job, I really do. I don't really care so much about the software I am writing. Don't get me wrong, I am proud of my code, but what I produce has no value outside of its context. The value comes from all those other people that are part of the work we do: the directors and clients with their wild imaginations and forward thinking, the designers and their creative solutions to the seemingly impossible problems, the project managers and Scrum masters that keep our eyes on the prize and all the other stakeholders that make sure we build what people didn't know they needed.&lt;/p&gt;

&lt;p&gt;On a huge scale, the achievement of the Event Horizon Telescope project is an example of this. No one human can imagine a black hole all by themselves, let alone take a picture of it. But the whole of humanity surely can. Pushing boundaries is a team effort.&lt;/p&gt;

&lt;p&gt;Now for the music. After this week's &lt;a href="https://www.theverge.com/2019/4/13/18308652/katie-bouman-black-hole-science-internet"&gt;shitty controversy regarding Katie Bouman's work&lt;/a&gt; I was reminded of this Björk quote from &lt;a href="https://pitchfork.com/features/interview/9582-the-invisible-woman-a-conversation-with-bjork/"&gt;a 2015 Pitchfork interview&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I want to support young girls who are in their 20s now and tell them: You’re not just imagining things. It’s tough. Everything that a guy says once, you have to say five times. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So let me do the work of five women at once: Björk was right. Do something about it. Start with her awesome song &lt;em&gt;Crystalline&lt;/em&gt; from the topically space-and-nature-themed album &lt;em&gt;Biophilia&lt;/em&gt; and work from there. The outro alone is worth the listen.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/2PNzytx9EV0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Have a nice week!&lt;/p&gt;

</description>
      <category>science</category>
      <category>software</category>
      <category>teamwork</category>
      <category>music</category>
    </item>
    <item>
      <title>Pure Bliss With Pure Functions in Java</title>
      <dc:creator>Piet van Dongen</dc:creator>
      <pubDate>Fri, 15 Feb 2019 21:45:24 +0000</pubDate>
      <link>https://dev.to/pietvandongen/pure-bliss-with-pure-functions-in-java-1mba</link>
      <guid>https://dev.to/pietvandongen/pure-bliss-with-pure-functions-in-java-1mba</guid>
      <description>&lt;p&gt;Writing software is hard.&lt;br&gt;
Writing &lt;em&gt;good&lt;/em&gt; software is harder.&lt;br&gt;
Writing &lt;em&gt;good, simple&lt;/em&gt; software is the hardest.&lt;br&gt;
Writing &lt;em&gt;good, simple&lt;/em&gt; software &lt;em&gt;as a team&lt;/em&gt; is the hardest… est.&lt;/p&gt;

&lt;p&gt;You shouldn't need several hours to understand what a method, class, or package does. If you need a lot of mental effort to even start programming, you will run out of energy before you can produce quality. Reduce the cognitive load of your code and you will reduce its amount of bugs.&lt;/p&gt;

&lt;p&gt;“This is puristic nonsense!” you say. “Intellectual masturbation! Software should be modeled after real-world, mutable objects!”&lt;/p&gt;

&lt;p&gt;I'm not saying you should go on a vision quest and return as a hardcore functional programmer. That wouldn't be very productive. Functional and object-oriented programming can complement each other greatly.&lt;/p&gt;

&lt;p&gt;I will show you how.&lt;/p&gt;

&lt;p&gt;But first, let's get to the &lt;em&gt;what&lt;/em&gt; and &lt;em&gt;why&lt;/em&gt; of it.&lt;/p&gt;
&lt;h2&gt;
  
  
  What?
&lt;/h2&gt;

&lt;p&gt;A pure function is a function that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;…always returns the same result, given the same input.&lt;/li&gt;
&lt;li&gt;…does not have any side effects.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Put simply: When calling a pure function with input &lt;strong&gt;A&lt;/strong&gt;, it always returns &lt;strong&gt;B&lt;/strong&gt;, no matter how often, when and where you call it. Also, it does &lt;em&gt;nothing else&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EVbthI4q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/5yi733iw58c4vahfz59u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EVbthI4q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/5yi733iw58c4vahfz59u.png" alt="A pure function" width="768" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Java, a pure function might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;a&lt;/code&gt; is &lt;code&gt;2&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; is &lt;code&gt;3&lt;/code&gt;, the result is &lt;em&gt;always&lt;/em&gt;&lt;code&gt;5&lt;/code&gt;, no matter how often or how fast you call it, even if done so concurrently.&lt;/p&gt;

&lt;p&gt;As a counterexample, this would be an &lt;em&gt;impure&lt;/em&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Random&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;nextInt&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its output could be anything, no matter the input. It violates the first rule. If this would be part of a program, the program would become nigh impossible to reason about.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Yo16lNR9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/ua0886l34zk1zom33eb7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Yo16lNR9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/ua0886l34zk1zom33eb7.png" alt="An impure function" width="768" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An example of the second rule, a function that has side effects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;writeSomethingToFile&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even though this method is static and always returns the same value, it is impure. It does more than it advertises. Even worse, it does so secretly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---xKvlH-I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/gs5w5e078zp3nxe4l9dw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---xKvlH-I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/gs5w5e078zp3nxe4l9dw.png" alt="An impure function with side effects" width="768" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These examples may look simple and harmless. But impurity adds up quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why?
&lt;/h2&gt;

&lt;p&gt;Let's say you have a decent-sized program you want to add a new feature to. Before you can do so, you'll have to know what it &lt;em&gt;does&lt;/em&gt;. You will probably start by looking at the code, reading documentation, following method calls, et cetera.&lt;/p&gt;

&lt;p&gt;While you are doing this, you are creating a mental model of the program. You are trying to fit every possible flow of the data inside your mind. A human brain is not optimised for this task. After a few state changes here, some conditionals there, you start to get somewhat foggy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lDo-aa5C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/0qxtkd2twnhx8xp44agc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lDo-aa5C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/0qxtkd2twnhx8xp44agc.png" alt="Spaghetti code" width="768" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we have a breeding ground for bugs.&lt;/p&gt;

&lt;p&gt;So how can we prevent this? By reducing the complexity of our program, for starters. Pure functions can be of great help here.&lt;/p&gt;

&lt;h2&gt;
  
  
  How?
&lt;/h2&gt;

&lt;p&gt;A great way to clarify complex code is breaking it up into smaller, more manageable pieces. Ideally, every bit has its own responsibility. This makes them easier to reason about, especially if we can test them in isolation.&lt;/p&gt;

&lt;p&gt;While untangling the spaghetti, you will notice you are now able to free up some of your intellectual capabilities. You are slowly getting at the core of the problem. Every piece that can stand on its own can be moved and you are left with the essence of your program.&lt;/p&gt;

&lt;p&gt;Finding that bug is much easier now. So is adding a cool new feature. To make things more concrete, here are some guidelines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Partition
&lt;/h3&gt;

&lt;p&gt;Firstly, partition the bigger pieces by their functionality. Think about the various parts of the solution and how they interconnect.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WhRbij6A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/w1cb41i9713yd8dkg0a7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WhRbij6A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/w1cb41i9713yd8dkg0a7.png" alt="Untangling the spaghetti" width="768" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Break Up
&lt;/h3&gt;

&lt;p&gt;Break up those parts into composable units. Define a function that can be used to filter items from a list, or an action that can be re-used for every single item. Maybe add helper functions that encapsulate logic that would otherwise end up inside deeply nested code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--A4SL-S3f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/8rysdenxuxv6dyd7mnkz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A4SL-S3f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/8rysdenxuxv6dyd7mnkz.png" alt="Partitioning the problem" width="768" height="236"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Document
&lt;/h3&gt;

&lt;p&gt;Write documentation when you feel you are ‘done’ implementing a unit. This will help you see the logic from a different perspective and reveals unforeseen edge cases. If necessary, add unit tests to further define the program's intents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--p6t2AVuH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/fx9jh3x5tpbe07phcbuo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p6t2AVuH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/fx9jh3x5tpbe07phcbuo.png" alt="Documenting a pure function" width="768" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Rinse and repeat.&lt;/p&gt;

&lt;p&gt;The definition of &lt;em&gt;done&lt;/em&gt; is personal and can change over time. Don't overdo it. If it's still not clear enough, you will find out later. If not during a code review, maybe when adding a new feature to the project.&lt;/p&gt;

&lt;p&gt;So far, so good, right? Theory always sounds great. But we need some convincing in the form of a practical example.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Example
&lt;/h2&gt;

&lt;p&gt;Let's say we have a client that sells IoT devices. Customers can install these devices in their home and connect them to the internet. They can control these devices with an app on their mobile phone.&lt;/p&gt;

&lt;p&gt;Our client wants their customers to receive a push notification if their device has been offline for a while, so they have a chance to reconnect the device. If it stays offline, they want to push notifications periodically, to remind the customer to reconnect the device. But not too often, since they don't want unstable devices to cause an endless stream of warnings.&lt;/p&gt;

&lt;p&gt;Also, our customer wants to be able to adjust the thresholds for sending these messages. At runtime, without having to pay us to do it for them. There should be at least one threshold, but there could be any number of them.&lt;/p&gt;

&lt;p&gt;Using our backend software, we can detect the online status of these IoT devices. If they go offline, we store the moment they went offline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Naive Implementation
&lt;/h3&gt;

&lt;p&gt;How can we implement this? This feature looks quite simple on paper, so we could just start and see how far we'd get.&lt;/p&gt;

&lt;p&gt;After doing some scaffolding we quickly get to the core of the problem: determining whether notifications should be sent for each offline device. Sounds simple, right?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// @todo Send notifications for offline devices&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We add some for loops here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Device&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;offlineDevice&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;offlineDevices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entrySet&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some if statements there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;firstThresholdWasPassed&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking good!&lt;/p&gt;

&lt;p&gt;Wait, there's a special case for the last threshold.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oh, and one for a single threshold.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Crap, we forgot to check if the notification for each threshold was already sent. In 3 places.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;lastOfflineNotificationInstant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isPresent&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And what if it was sent before the device went offline?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;between&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;disconnectInstant&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lastOfflineNotificationInstant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;isNegative&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/pietvandongen/pure-bliss-with-pure-java-functions/blob/master/src/main/java/com/pietvandongen/purebliss/offlinedetector/job/naive/OfflineDevicesJobImpl.java#L42"&gt;Oh my&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.sonarlint.org/"&gt;SonarLint&lt;/a&gt; is foaming at the mouth, telling us to reduce the cognitive complexity of 69, where 15 is allowed. &lt;a href="https://github.com/pietvandongen/pure-bliss-with-pure-java-functions/blob/master/src/test/java/com/pietvandongen/purebliss/offlinedetector/job/naive/OfflineDevicesJobTests.java"&gt;The unit tests&lt;/a&gt; are getting quite complex as well, having to use an external &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/time/Clock.html"&gt;Clock&lt;/a&gt; an whatnot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VtVIeddD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/k59ixxi8ruklsbmdw1zm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VtVIeddD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/k59ixxi8ruklsbmdw1zm.png" alt="A correct, but messy implementation" width="768" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We cannot let our future selves see this, they'd kill us!&lt;/p&gt;

&lt;p&gt;Luckily, we have our three-step program. Let's begin at 1.&lt;/p&gt;

&lt;h3&gt;
  
  
  Partitioning by Functionality
&lt;/h3&gt;

&lt;p&gt;Now, to untangle this mess. Our first step is to &lt;strong&gt;partition&lt;/strong&gt; the bigger pieces by functionality, to think about the various parts of the solution and how they interconnect.&lt;/p&gt;

&lt;p&gt;We could visualize our problem by drawing a timeline, starting at the instant a device went offline:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--otQH55je--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/9lqiuthguys59lq30o7m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--otQH55je--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/9lqiuthguys59lq30o7m.png" alt="A timeline" width="768" height="254"&gt;&lt;/a&gt; On the timeline we can plot the various thresholds:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bmyeOf7u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/n5gjes7fe5a04o00fn2i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bmyeOf7u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/n5gjes7fe5a04o00fn2i.png" alt="A timeline with thresholds" width="768" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every time our job is running, we have arrived at a certain point on the timeline. If we have not yet passed a threshold, nothing needs to be done:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Qq89nIaA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/t4omltnmhmp6uesal5rm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Qq89nIaA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/t4omltnmhmp6uesal5rm.png" alt="Checking for passed thresholds" width="768" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we do pass a threshold, we should send a notification and remember the time we sent it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ViNElbr8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/2dswmqoqgdmo1fa7m972.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ViNElbr8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/2dswmqoqgdmo1fa7m972.png" alt="Sending and saving a notification for a passed threshold" width="768" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next time we run our job, we should take the sent notification into account. If we have passed a threshold that the user was already notified about, we do nothing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KX7YYZp6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/vi7735yr2zh40dg8btuj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KX7YYZp6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/vi7735yr2zh40dg8btuj.png" alt="Not sending a notification for a passed threshold" width="768" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Only when passing another threshold, we can send another notification:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IaRnyTtG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/mz4u4ad8pgounfxwgf89.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IaRnyTtG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/mz4u4ad8pgounfxwgf89.png" alt="Sending a notification for a newly passed threshold" width="768" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And so on and so forth.&lt;/p&gt;

&lt;p&gt;How do we express this in code? It seems we need to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which threshold (if any) we passed last.&lt;/li&gt;
&lt;li&gt;Which threshold (if any) we sent the last notification for.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it, basically. If we know these, we can determine if we should send a notification. All the special cases and complexity are captured by these two core parts of our solution.&lt;/p&gt;

&lt;p&gt;Now we need to see if we can simplify our solution any further by breaking it up into composable parts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Breaking It Up Into Composable Units
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ONfk83gT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/zuxuuewwqlh0ho60ynzm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ONfk83gT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/zuxuuewwqlh0ho60ynzm.png" alt="Composing pure functions" width="768" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The two above statements look awfully familiar, no? That's because they are. They both define a threshold or nothing. They both need similar input:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The moment the device went offline.&lt;/li&gt;
&lt;li&gt;The threshold(s).&lt;/li&gt;
&lt;li&gt;The point in time we are currently evaluating.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we know the input and output of our unit, we can define its signature. Using the fancy &lt;code&gt;java.time&lt;/code&gt; API, we can express it as such:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Optional&lt;/span&gt; &lt;span class="nf"&gt;calculateLastPassedThreshold&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now use this function to get both thresholds we needed. Did I say function? Let's make that a &lt;em&gt;pure function&lt;/em&gt;: declare it static and make sure it's deterministic and doesn't create any side effects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;calculateLastPassedThreshold&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Duration&lt;/span&gt; &lt;span class="n"&gt;timePassed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;between&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timePassed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compareTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;empty&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timePassed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compareTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There! Aside from its breathtaking purity, it has another pro: &lt;a href="https://github.com/pietvandongen/pure-bliss-with-pure-java-functions/blob/master/src/test/java/com/pietvandongen/purebliss/offlinedetector/job/brokenup/OfflineDevicesJobTests.java#L78"&gt;It can be unit tested very easily&lt;/a&gt;. You don't need mocks nor external clocks to test this part of the code.&lt;/p&gt;

&lt;p&gt;Can you feel the amount of space your brain just regained? When programming the rest of our solution, we can just trust that this function will do what it says.&lt;/p&gt;

&lt;p&gt;So what's left?&lt;/p&gt;

&lt;p&gt;We can now determine, for each device, if we should send a notification. Again, we can isolate this part of our program, make it pure and test it thoroughly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;shouldSendNotification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;jobStart&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;deviceOffline&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;lastNotification&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lastPassedThreshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;calculateLastPassedThreshold&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deviceOffline&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jobStart&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;lastPassedThreshold&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isPresent&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lastNotification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isBefore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deviceOffline&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lastPassedThresholdNotifiedAbout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;calculateLastPassedThreshold&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deviceOffline&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lastNotification&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;lastPassedThreshold&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lastPassedThresholdNotifiedAbout&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, more concisely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;shouldSendNotification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;jobStart&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;deviceOffline&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;lastNotification&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lastPassedThreshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;calculateLastPassedThreshold&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deviceOffline&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jobStart&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;lastPassedThreshold&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isPresent&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lastNotification&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isBefore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deviceOffline&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;lastPassedThreshold&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;calculateLastPassedThreshold&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deviceOffline&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lastNotification&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when you know the device has no previously sent notifications, you can just use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;shouldSendNotification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;jobStart&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;deviceOffline&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;calculateLastPassedThreshold&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deviceOffline&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jobStart&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;isPresent&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the only thing left is calling these functions for every offline device, every time our job runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;jobStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;offlineDevices&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entrySet&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;offlineDevice&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pushNotificationService&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLastOfflineNotificationInstant&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;offlineDevice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getKey&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instant&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;shouldSendNotification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jobStart&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offlineDevice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;instant&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElseGet&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;shouldSendNotification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jobStart&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offlineDevice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;offlineDevice&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pushNotificationService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendOfflineNotification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;offlineDevice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getKey&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're bordering on &lt;em&gt;&lt;a href="https://www.reddit.com/r/iamverysmart/"&gt;iamverysmart&lt;/a&gt;&lt;/em&gt;-territory here. Not everyone likes this style of coding and it is arguably harder to parse than a more traditional style. So the least we can do is document our units.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documenting Our Solution
&lt;/h3&gt;

&lt;p&gt;Aside from playing nice with others, describing our code can also help us catch that last bug, or reveal an edge case we hadn't thought of yet.&lt;/p&gt;

&lt;p&gt;Documentation in our case most obviously comes in the form of Javadoc, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Checks whether a notification should be sent by determining which threshold has been passed last for the
 * calculated amount of time passed between the device going offline and the job running.
 *
 * @param jobStart         The instant the job calling this function was started.
 * @param deviceOffline    The instant the device went offline.
 * @param lastNotification The instant the last notification was sent.
 * @param thresholds       The list of notification thresholds.
 * @return True if the notification should be sent, false if not.
 */&lt;/span&gt;
&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;shouldSendNotification&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;jobStart&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;deviceOffline&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;lastNotification&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;thresholds&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unit tests can also be regarded as documentation since they can define the intended use of the program under test very precisely. In the case of pure functions, where you often need to test a list of varying input, &lt;a href="https://github.com/junit-team/junit4/wiki/parameterized-tests"&gt;parameterized tests&lt;/a&gt; can come in handy.&lt;/p&gt;

&lt;p&gt;While writing tests and documentation, we shift our thinking from the problem to the solution, and its users. By thinking from this other perspective, &lt;a href="https://github.com/pietvandongen/pure-bliss-with-pure-java-functions/blob/master/src/test/java/com/pietvandongen/purebliss/offlinedetector/job/documented/OfflineDevicesJobTests.java#L27"&gt;subtle problems in our code&lt;/a&gt; are highlighted and can be fixed on the spot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Afterthoughts
&lt;/h2&gt;

&lt;p&gt;It can be hard to convince others or even yourself of the value of this approach. Churning out functionality has more obvious business value than meticulously crafting software.&lt;/p&gt;

&lt;p&gt;When approaching existing code this way, you can end up wasting hours while adding no measurable (short-term) value. Try explaining that during the daily stand-up.&lt;/p&gt;

&lt;p&gt;On the other hand, preventing a bug is cheaper than fixing it, and the more &lt;em&gt;clean&lt;/em&gt; your code is, the less time it takes to add a feature or find an error, especially if the code hasn't been touched in a while.&lt;/p&gt;

&lt;p&gt;Somewhere along the line, the balance is tipped. On one side lies the holy grail, the unachievable perfect piece of software. On the other side, the unmaintainable spaghetti monster slithers, shitting on deadlines, ballooning budgets and destroying teams.&lt;/p&gt;

&lt;p&gt;Listen to your instincts, communicate your intentions, listen to your peers and learn from one another: making great software is an ongoing (team) effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source code
&lt;/h2&gt;

&lt;p&gt;All code referenced in this post is open source and can be found in its &lt;a href="https://github.com/pietvandongen/pure-bliss-with-pure-java-functions"&gt;GitHub repository&lt;/a&gt;. Feel free to check it out, tinker with it and come up with better solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback
&lt;/h2&gt;

&lt;p&gt;I'd love to hear your feedback in the form of &lt;a href="https://github.com/pietvandongen/pure-bliss-with-pure-java-functions/pulls"&gt;pull requests&lt;/a&gt;, &lt;a href="https://github.com/pietvandongen/pure-bliss-with-pure-java-functions/issues"&gt;issues&lt;/a&gt;, or comments! There are some nice discussions going on over here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://sharing.luminis.eu/blog/pure-bliss-with-pure-functions-in-java/"&gt;The comment section of this article on its original blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dzone.com/articles/pure-bliss-with-pure-functions-in-java"&gt;The comment section of this article on Dzone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.reddit.com/r/programming/comments/793ejt/pure_bliss_with_pure_functions_in_java/"&gt;reddit.com/r/programming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.reddit.com/r/java/comments/793gto/pure_bliss_with_pure_functions_in_java/"&gt;reddit.com/r/java&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or you can post your comments below.&lt;/p&gt;

</description>
      <category>java</category>
      <category>functional</category>
      <category>programming</category>
      <category>refactoring</category>
    </item>
  </channel>
</rss>
