<?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: BjornvdLaan</title>
    <description>The latest articles on DEV Community by BjornvdLaan (@bjornvdlaan).</description>
    <link>https://dev.to/bjornvdlaan</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%2F724445%2F303d01ee-506c-4198-8d31-94886fdd702e.png</url>
      <title>DEV Community: BjornvdLaan</title>
      <link>https://dev.to/bjornvdlaan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bjornvdlaan"/>
    <language>en</language>
    <item>
      <title>Green Software is Event Driven and Carbon Aware, with Kubernetes and KEDA</title>
      <dc:creator>BjornvdLaan</dc:creator>
      <pubDate>Thu, 07 Sep 2023 15:41:17 +0000</pubDate>
      <link>https://dev.to/bjornvdlaan/green-software-is-carbon-aware-and-event-driven-with-kubernetes-and-keda-5bk4</link>
      <guid>https://dev.to/bjornvdlaan/green-software-is-carbon-aware-and-event-driven-with-kubernetes-and-keda-5bk4</guid>
      <description>&lt;p&gt;Did you know that data centres emit more carbon than aviation or shipping? And it is not slowing down, with some &lt;a href="https://www.sciencedirect.com/science/article/abs/pii/S095965261733233X"&gt;research&lt;/a&gt; forecasting that computing will contribute 14% of global emissions by 2040. Even worse, a significant portion of these emissions is wasted on non-production resources running outside office hours, always having your application scaled out for peak hours. This is not only bad for the planet but also for your cloud bill.&lt;/p&gt;

&lt;p&gt;How can we do better? Instead of having a fixed number of application replicas, we should match the scale of our applications to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Current events, for example, in terms of incoming web requests or messages in a Kafka topic. Sizing your application based on how much work it has alleviates overprovisioning and always-on resources.&lt;/li&gt;
&lt;li&gt;The availability of clean energy. The electricity on the data centre's power grid comes from different sources, such as coal, nuclear, and solar. We should do more when the energy is cleaner, i.e., the &lt;a href="https://learn.greensoftware.foundation/carbon-awareness#carbon-intensity"&gt;carbon intensity&lt;/a&gt; is lower to reduce emissions further.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This article shows how we can make our applications greener by &lt;br&gt;
 making them event-driven and carbon-aware. We'll introduce these properties at the level of the Kubernetes cluster using &lt;a href="https://keda.sh/"&gt;KEDA&lt;/a&gt; (Kubernetes Event-Driven Autoscaler) and Microsoft's &lt;a href="https://github.com/Azure/carbon-aware-keda-operator"&gt;Carbon Aware KEDA Operator&lt;/a&gt;, so all applications in the cluster can benefit without becoming more complex themselves. All code examples are found in my &lt;a href="https://github.com/BjornvdLaan/carbon-aware-keda-kubernetes"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The webshop that wants to become greener
&lt;/h2&gt;

&lt;p&gt;Consider a simple webshop with only two applications: a webserver that serves the website to customers and an ordering system that takes care of all other things. When a customer places an order, the webserver puts a message with all necessary information on a queue and immediately thanks the customer for its purchase. The ordering system consumes these messages asynchronously, checks the warehouse, dispatches the delivery, and then sends a confirmation email to the customer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tPYHomOM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o3zm8vys1x1uuao2sbxh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tPYHomOM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o3zm8vys1x1uuao2sbxh.png" alt="Webserver and Ordering System communicate through queue" width="800" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even though the number of customers varies throughout the day, both applications are currently deployed with a fixed number of replica pods appropriate for busy moments. Below is a graph with the number of messages in the queue and the ordering system replicas in the cluster. The red line indicates the number of messages on the queue, and blue shows the number of pods.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ew5s3Bx0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d1q8ejaijfkxgguy9i18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ew5s3Bx0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d1q8ejaijfkxgguy9i18.png" alt="Graph showing events and number of replicas (base scenario)" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For simplicity, we assume that the replicas and messages at a specific y-coordinate would be exactly in balance, i.e., precisely enough replicas for that number of messages. In that case, the area under the blue and red lines relates to the total and useful resource consumption, respectively. The difference is wasted resources.&lt;/p&gt;

&lt;p&gt;The webshop takes pride in its sustainable reputation and also wants to cut back on cloud costs. The webshop owner spoke with customers and learned that while the webserver should always be available and fast, it's okay if the asynchronous email confirmation arrives a bit later.&lt;/p&gt;

&lt;p&gt;The webshop owner wants to scale the ordering system based on the queue size and whether clean energy is available. In other words, when carbon intensity is low, and many messages are waiting in the queue, the ordering system can go full speed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nrFmnRV---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ly9p8rm2ltd465scvsd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nrFmnRV---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ly9p8rm2ltd465scvsd.png" alt="Maximum number of replicas if many events and low carbon intensity" width="800" height="940"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If, on the other hand, either the carbon intensity is high or the number of messages is low, we should balance the number of replicas:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BKcqwZ3X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4qncdz93sb2bsza4igts.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BKcqwZ3X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4qncdz93sb2bsza4igts.png" alt="Balanced number of replicas if less events or higher carbon intensity" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's explore how we can achieve this with KEDA.&lt;/p&gt;
&lt;h2&gt;
  
  
  Event-driven scaling of pods
&lt;/h2&gt;

&lt;p&gt;We want to move away from an always-on ordering system with too many replicas. To dynamically scale the ordering system based on the number of messages in the queue, we use &lt;a href="https://keda.sh/"&gt;KEDA&lt;/a&gt; (Kubernetes Event-Driven Autoscaler), a Kubernetes operator that can scale our application based on 'events'. In our case, 'events' refer to the messages on the queue, but many more events are &lt;a href="https://keda.sh/docs/2.11/scalers/"&gt;supported&lt;/a&gt;. With the KEDA Controller installed in the cluster, we can dynamically scale the ordering system by creating a &lt;code&gt;ScaledObject&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keda.sh/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ScaledObject&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ordering-system-scaler&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scaleTargetRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ordering-system&lt;/span&gt;

  &lt;span class="na"&gt;pollingInterval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
  &lt;span class="na"&gt;cooldownPeriod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
  &lt;span class="na"&gt;idleReplicaCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
  &lt;span class="na"&gt;minReplicaCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;maxReplicaCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
  &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;failureThreshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
    &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;

  &lt;span class="na"&gt;triggers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-sqs-queue&lt;/span&gt;
      &lt;span class="na"&gt;authenticationRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ordering-system-triggerauth&lt;/span&gt;
      &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;queueURL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;AWS-DOMAIN&amp;gt;/000000000000/ordering-queue&lt;/span&gt;
        &lt;span class="na"&gt;awsEndpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;AWS-DOMAIN&amp;gt;&lt;/span&gt;
        &lt;span class="na"&gt;queueLength&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;50"&lt;/span&gt;
        &lt;span class="na"&gt;awsRegion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eu-west-1"&lt;/span&gt;
        &lt;span class="na"&gt;identityOwner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pod&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The YAML above consists of roughly three parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We must tell the &lt;code&gt;ScaledObject&lt;/code&gt; which application we want to scale. This is done through the &lt;code&gt;scaleTargetRef&lt;/code&gt; key. In this case, the target &lt;code&gt;ordering-system&lt;/code&gt; is the &lt;code&gt;Deployment&lt;/code&gt; that manages the ordering system replicas.&lt;/li&gt;
&lt;li&gt;Then, we configure &lt;em&gt;how&lt;/em&gt; we want to scale the application. &lt;code&gt;minReplicaCount&lt;/code&gt; and &lt;code&gt;maxReplicaCount&lt;/code&gt; determine the range of scaling, and we can configure many other properties as well.&lt;/li&gt;
&lt;li&gt;Finally, we tell which event(s) to scale on in &lt;code&gt;triggers&lt;/code&gt;. Here, we defined the AWS SQS Queue trigger and told it to strive for 50 messages in the queue.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The graph from earlier changes as follows:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--obi-Q1wZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kbesnehlnjce9og7zby6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--obi-Q1wZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kbesnehlnjce9og7zby6.png" alt="Graph showing events and number of replicas (event-driven scenario)" width="800" height="350"&gt;&lt;/a&gt;&lt;br&gt;
The difference in area under both lines is much smaller now, indicating more efficient resource usage.&lt;/p&gt;

&lt;p&gt;What I really like about KEDA is that it does not try to reinvent the wheel. Instead, it reuses Kubernetes' built-in &lt;a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/"&gt;HPA&lt;/a&gt; (Horizontal Pod Autoscaler). Since &lt;a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#autoscaling-on-metrics-not-related-to-kubernetes-objects"&gt;Kubernetes 1.10&lt;/a&gt;, the HPA can use &lt;code&gt;external&lt;/code&gt; metrics based on External Metrics Servers. KEDA 'simply' implements an External Metrics Server that exposes data about the triggers we define in our &lt;code&gt;ScaledObject&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Carbon-aware limits to scaling
&lt;/h2&gt;

&lt;p&gt;With KEDA's event-driven scaling, our resource consumption already changed drastically. Still, the ordering system does not take into account the current availability of clean energy. If the webshop is busier on moments with less clean energy, we can still emit much more than we would like. Let's see how we can add carbon-awareness using the &lt;a href="https://github.com/Azure/carbon-aware-keda-operator"&gt;Carbon Aware KEDA Operator&lt;/a&gt;. This Kubernetes operator limits how far KEDA can scale our consumer based on the carbon-intensity. This operator essentially sets a ceiling for allowed &lt;code&gt;maxReplicas&lt;/code&gt; in KEDA's &lt;code&gt;ScaledObject&lt;/code&gt; based on the carbon intensity of the current time, allowing more scaling when carbon intensity is lower. Once the operator is installed, we can configure the scaling limits through a custom resource definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;carbonaware.kubernetes.azure.com/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CarbonAwareKedaScaler&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;carbon-aware-ordering-system-scaler&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;maxReplicasByCarbonIntensity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;            
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;carbonIntensityThreshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;237&lt;/span&gt;
      &lt;span class="na"&gt;maxReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;carbonIntensityThreshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;459&lt;/span&gt;
      &lt;span class="na"&gt;maxReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;carbonIntensityThreshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;570&lt;/span&gt;
      &lt;span class="na"&gt;maxReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;

  &lt;span class="na"&gt;kedaTarget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;scaledobjects.keda.sh&lt;/span&gt;
  &lt;span class="na"&gt;kedaTargetRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ordering-system-scaler&lt;/span&gt;
    &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;

  &lt;span class="na"&gt;carbonIntensityForecastDataSource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;       
    &lt;span class="na"&gt;mockCarbonForecast&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;               
    &lt;span class="na"&gt;localConfigMap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;                        
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;carbon-intensity&lt;/span&gt;
      &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kube-system&lt;/span&gt;
      &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similar to how the &lt;code&gt;ScaledObject&lt;/code&gt; targeted a &lt;code&gt;Deployment&lt;/code&gt; through its &lt;code&gt;scaleTargetRef&lt;/code&gt;, this &lt;code&gt;CarbonAwareKedaScaler&lt;/code&gt; targets a &lt;code&gt;ScaledObject&lt;/code&gt; defined in &lt;code&gt;kedaTargetRef&lt;/code&gt;. This makes for a neat layered approach where each layer is unaware of the layer above.&lt;/p&gt;

&lt;p&gt;The essential part of the YAML definition above is the &lt;code&gt;maxReplicasByCarbonIntensity&lt;/code&gt; key, which lets us configure increasingly strict scaling limits based on the availability of clean energy.&lt;/p&gt;

&lt;p&gt;The operator retrieves the current carbon intensity from the &lt;code&gt;ConfigMap&lt;/code&gt; we define under the &lt;code&gt;carbonIntensityForecastDataSource&lt;/code&gt; key, but actually filling the &lt;code&gt;ConfigMap&lt;/code&gt; is not something it can do for you. Fortunately, Microsoft created a companion operator called the &lt;a href="https://github.com/Azure/kubernetes-carbon-intensity-exporter/"&gt;Carbon Intensity Exporter&lt;/a&gt;, which builds on the Green Software Foundations's &lt;a href="https://github.com/Green-Software-Foundation/carbon-aware-sdk"&gt;CarbonAwareSDK&lt;/a&gt; to provide carbon intensity data in the Kubernetes cluster. However, if you just want to play around then you can also set the &lt;code&gt;mockCarbonForecast&lt;/code&gt; key to &lt;code&gt;true&lt;/code&gt;. The operator will then create a &lt;code&gt;ConfigMap&lt;/code&gt; with fake carbon intensity data for testing purposes.&lt;/p&gt;

&lt;p&gt;With the Carbon Aware KEDA Operator configured in our cluster, we further decrease our webshop's carbon emissions by doing more when energy is cleaner: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L2Y5MnRf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ccvdx02yq0rhew2a8bin.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L2Y5MnRf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ccvdx02yq0rhew2a8bin.png" alt="Graph showing events and number of replicas (event-driven and carbon-aware scenario)" width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the second peak, the messages are consumed at a slightly slower pace because carbon intensity is too high at that moment. The customer might need to wait a few more seconds for the confirmation email but likely won't even notice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Computing significantly contributes to global carbon emissions, and digitalisation shows no signs of slowing down. We should, therefore, become more efficient in our resource usage. This article shows how we can use KEDA to become more event-driven and carbon-aware and reduce overprovisioning of our applications. &lt;/p&gt;

</description>
      <category>greensoftware</category>
      <category>kubernetes</category>
      <category>keda</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Carbon Aware Software: Three Demand Shaping Strategies, implemented in Kotlin</title>
      <dc:creator>BjornvdLaan</dc:creator>
      <pubDate>Mon, 10 Jul 2023 08:22:31 +0000</pubDate>
      <link>https://dev.to/bjornvdlaan/carbon-aware-software-three-demand-shaping-strategies-implemented-in-kotlin-3jki</link>
      <guid>https://dev.to/bjornvdlaan/carbon-aware-software-three-demand-shaping-strategies-implemented-in-kotlin-3jki</guid>
      <description>&lt;p&gt;Software engineers have a largely untapped opportunity to contribute to a greener world. In this article, I discuss how we can schedule asynchronous tasks to minimise carbon emissions.&lt;/p&gt;

&lt;p&gt;Electricity comes from various sources, each with different carbon emissions. Depending on the source, a certain amount of carbon is emitted for each generated kilowatt hour (kwh). Clean and renewable sources like wind, solar, and hydroelectric power emit minimal carbon, while fossil fuel sources emit quite a bit more. The amount of carbon emitted by an energy source is called its carbon intensity, measured in grams of carbon per kwh.&lt;/p&gt;

&lt;p&gt;Ideally, you would plug your computer or data center directly into a wind farm so that your software is powered with zero carbon intensity. However, it is not feasible for most individuals to directly connect to wind farms; instead, they rely on power grids that receive electricity from a mix of sources. Once connected to a grid, you cannot control the specific sources supplying your electricity. You simply receive a blend of all the current power sources within the grid, including both lower- and higher-carbon sources. Therefore, the carbon intensity of the electricity you consume reflects a combination of all the sources on the grid at that moment. &lt;/p&gt;

&lt;p&gt;Carbon awareness is the idea of doing more when more energy comes from low-carbon sources and doing less when more energy comes from high-carbon sources. One approach to building carbon-aware software is called &lt;strong&gt;demand shaping&lt;/strong&gt;: we shape our computation to match the existing supply. When the carbon cost of running your application becomes high, shape the demand to match the supply of carbon.&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%2F11yrci5h684sulj6j9eu.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%2F11yrci5h684sulj6j9eu.png"&gt;&lt;/a&gt;&lt;br&gt;Demand shaping (&lt;a href="https://learn.greensoftware.foundation/carbon-awareness" rel="noopener noreferrer"&gt;https://learn.greensoftware.foundation/carbon-awareness&lt;/a&gt;)
  &lt;/p&gt;

&lt;p&gt;Demand shaping sounded a bit abstract to me when I first heard about it. Therefore, I decided to build carbon-aware software myself using Kotlin and Spring Boot. This article shows my plans and insights. You can find my code &lt;a href="https://github.com/BjornvdLaan/carbon-aware-consumer" rel="noopener noreferrer"&gt;here at GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our implementation
&lt;/h2&gt;

&lt;p&gt;We'll simulate a scenario where a producer periodically puts task descriptions on a queue, describing a computationally intensive task that some consumer should execute asynchronously. Imagine this being some tasks that are not time-critical but should be performed somewhere in the future. Before executing a task, the consumers will first reach out to an API called the CarbonAwareSDK, which provides information about the carbon intensity at a certain time and place. &lt;/p&gt;

&lt;p&gt;Each consumer implements a different strategy for processing events based on the retrieved carbon intensity:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Threshold consumer&lt;/strong&gt; only consumes new tasks from the queue if the carbon intensity is lower than a certain threshold.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adaptive velocity consumer&lt;/strong&gt;, which takes longer breaks between tasks based on the carbon intensity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forecast-based consumer&lt;/strong&gt;, which runs its daily task at the optimal time in the next 24 hours.&lt;/li&gt;
&lt;/ol&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%2Fcskv65e1yj7bqwjt6kym.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%2Fcskv65e1yj7bqwjt6kym.png" alt="The carbon aware components setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  CarbonAwareSDK API
&lt;/h2&gt;

&lt;p&gt;CarbonAwareSDK helps us retrieve information about the carbon intensity at a certain time and place. Documentation on the available endpoints is found &lt;a href="https://github.com/Green-Software-Foundation/carbon-aware-sdk/blob/dev/docs/carbon-aware-webapi.md" rel="noopener noreferrer"&gt;here&lt;/a&gt;. For our purposes, we use two of its functionalities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Current Carbon Intensity
&lt;/h3&gt;

&lt;p&gt;We can request the current carbon intensity at a specific location through the following URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://carbon-aware-api.azurewebsites.net/emissions/bylocation?location=eastus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response contains a field &lt;code&gt;rating&lt;/code&gt;: the carbon intensity, measured in grams of CO2 equivalent per kilowatt hour.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;location:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PJM_ROANOKE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;time:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2023-06-26T17:05:00+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;rating:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;588.30930389&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;duration:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"00:05:00"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Forecasted carbon intensity
&lt;/h3&gt;

&lt;p&gt;We can also ask the service to forecast carbon intensities between two timestamps, indicated by path variables &lt;code&gt;dataStartAt&lt;/code&gt; and &lt;code&gt;dataEndAt&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://carbon-aware-api.azurewebsites.net/emissions/forecasts/current?location=northeurope&amp;amp;dataStartAt=2023-07-19T15:00:00Z&amp;amp;dataEndAt=2023-07-20T16:00:00Z&amp;amp;windowSize=10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One really cool feature is that it also gives us the most moment in that time frame where carbon intensity is lowest.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;generatedAt:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2023-06-26T17:00:00+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;requestedAt:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2023-06-26T17:03:33.4552082+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;location:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eastus"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;dataStartAt:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2023-06-26T17:05:00+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;dataEndAt:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2023-06-27T17:00:00+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;windowSize:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;optimalDataPoints:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="err"&gt;location:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PJM_ROANOKE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="err"&gt;timestamp:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2023-06-27T07:45:00+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="err"&gt;duration:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="err"&gt;value:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;497.9432056032178&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;forecastData:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="err"&gt;location:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PJM_ROANOKE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="err"&gt;timestamp:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2023-06-26T17:05:00+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="err"&gt;duration:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="err"&gt;value:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;607.0114067773352&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="err"&gt;location:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PJM_ROANOKE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="err"&gt;timestamp:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2023-06-26T17:10:00+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="err"&gt;duration:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="err"&gt;value:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;606.2321824825342&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="err"&gt;location:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PJM_ROANOKE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="err"&gt;timestamp:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2023-06-26T17:15:00+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="err"&gt;duration:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="err"&gt;value:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;605.3570016963745&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;more&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Threshold consumer
&lt;/h2&gt;

&lt;p&gt;This first consumer is called every 5 seconds using Spring Boot's &lt;code&gt;@Scheduled&lt;/code&gt; annotation. Upon invocation, the current carbon intensity is retrieved. Tasks are only retrieved and executed if the carbon intensity is lower than the threshold &lt;code&gt;MAXIMUM_CARBON_INTENSITY&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ThresholdConsumer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;taskProcessor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TaskProcessor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;carbonIntensityService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CarbonIntensityService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;queueService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;QueueService&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Scheduled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fixedDelay&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeUnit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SECONDS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;consume&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;currentCarbonIntensity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;carbonIntensityService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntensity&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;rating&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentCarbonIntensity&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nc"&gt;MAXIMUM_CARBON_INTENSITY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;task&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queueService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receiveTask&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;taskProcessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nc"&gt;LOGGER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Task ${it.id} executed successfully with threshold strategy. Result = ${result}."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;LOGGER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No task executed with threshold strategy because carbon intensity ${currentCarbonIntensity} is too high (threshold = ${MAXIMUM_CARBON_INTENSITY})."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;MAXIMUM_CARBON_INTENSITY&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;LOGGER&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoggerFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ThresholdConsumer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adaptive velocity consumer
&lt;/h2&gt;

&lt;p&gt;The adaptive velocity consumer is different from our first consumer, who will always continue consuming messages. It just works harder when the carbon intensity is lower. After reading and executing a task, the consumer sleeps 10 milliseconds for each gram of CO2 emitted for a kwh at this moment. It will therefore sleep less if more clean energy is available.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdaptiveVelocityConsumer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;taskProcessor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TaskProcessor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;carbonIntensityService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CarbonIntensityService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;queueService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;QueueService&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@EventListener&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;consume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ApplicationReadyEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;currentCarbonIntensity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;carbonIntensityService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getIntensity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;delay&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentCarbonIntensity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rating&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10L&lt;/span&gt;
            &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLong&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;task&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queueService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receiveTask&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;taskProcessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nc"&gt;LOGGER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Task ${it.id} executed successfully with adapted speed strategy. Result = ${result}."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;LOGGER&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoggerFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AdaptiveVelocityConsumer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Forecast consumer
&lt;/h2&gt;

&lt;p&gt;The first two consumers make decisions based on the current carbon intensity, which is quite similar to a greedy algorithm. Our final consumer aims to be smarter than that. Instead of asking CarbonAwareSDK for the current carbon intensity, the consumer requests a forecast for the coming day. The response looks as below. The most important field is &lt;code&gt;optimalDataPoints&lt;/code&gt;, which contains the best moment to perform our task.&lt;/p&gt;

&lt;p&gt;We schedule the consumer to be run once per day. During invocation, it retrieves the best moment to execute a task that day and schedules it using Spring Boot's &lt;code&gt;TaskScheduler&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ForecastingConsumer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;taskScheduler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TaskScheduler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;taskProcessor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TaskProcessor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;carbonIntensityService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CarbonIntensityService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;queueService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;QueueService&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Scheduled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fixedDelay&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeUnit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DAYS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;consume&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;task&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queueService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receiveTask&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;optimalIntensity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;carbonIntensityService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getForecastedOptimalIntensity&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;optimalDataPoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="n"&gt;taskScheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Instant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DateTimeFormatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ISO_INSTANT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;optimalIntensity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;taskProcessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nc"&gt;LOGGER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Task ${it.id} executed successfully with forecasting strategy. Result = ${result}."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nc"&gt;LOGGER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Task ${it.id} scheduled successfully at ${optimalIntensity.timestamp} with forecasting strategy."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


    &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;LOGGER&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoggerFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ForecastingConsumer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nc"&gt;TaskScheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Runnable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Insights
&lt;/h2&gt;

&lt;p&gt;These three consumers give us an initial idea of how we can build carbon-aware software. However, is the application level the right place to implement this logic? Yes, we manage to do more work when we emit less carbon, but we still have these applications running and methods being invoked. All these network requests between the consumers and the CarbonAwareSDK also costs electricity. In addition, the application becomes more complex with this shaping logic, degrading its carbon efficiency. The three consumers each also have specific downsides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The threshold consumer cannot guarantee that tasks are ever executed. Some locations in the world are still very reliant on coal so that the threshold might be too low.&lt;/li&gt;
&lt;li&gt;The adaptive velocity consumer reads faster when cleaner energy is available but could still process a lot of its tasks during very dirty times of the day. Even worse, it could slowly work through the whole queue during high carbon intensity hours and then have nothing left to do when it wants to speed up.&lt;/li&gt;
&lt;li&gt;The forecasting consumer solves the downsides of the previous two, but scheduling a task at the application level is a risky move. At any moment, the application could be killed as part of a rolling update or some error unrelated to the task. The scheduling is then lost. The task description was already removed from the queue, so it won't be executed anymore altogether. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This all gets even messier if we have multiple replicas of the same application, which is not uncommon in production environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shift down to the platform
&lt;/h2&gt;

&lt;p&gt;I believe the forecasting consumer is the most promising of the three strategies. However, the application is not the right place to implement this functionality. Instead, I imagine having a custom resource in Kubernetes that allows us to schedule tasks based on the carbon intensity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;carbonaware/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CarbonAwareDailyJob&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-day-job&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;jobTemplate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;consumer&lt;/span&gt;
            &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;task-consumer:1.0&lt;/span&gt;
          &lt;span class="na"&gt;restartPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;OnFailure&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;CarbonAwareDailyJob&lt;/code&gt; custom resource and its associated custom controller, reach out to the CarbonAwareSDK (which you can also host on-premise!) for the forecast of the next 24 hours. The Job described by the &lt;code&gt;jobTemplate&lt;/code&gt; is then executed at that optimal moment, very similar to the forecasting consumer we saw earlier. An added benefit is that our custom controller centralises all calls to the CarbonAwareSDK API, saving network traffic and carbon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Implementing carbon-aware software with demand shaping techniques has the potential to reduce carbon emissions and promote sustainability in our digital landscape. In this article, we applied demand shaping at the application level. However, I learned that demand shaping might be better implemented at a lower level. Green software patterns like demand shaping are still under active development. Continued research, collaboration, and adoption of carbon-aware software solutions are vital to realising these benefits and creating a greener future.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>greensoftware</category>
      <category>carbonaware</category>
      <category>programming</category>
    </item>
    <item>
      <title>3 steps towards a greener IT landscape</title>
      <dc:creator>BjornvdLaan</dc:creator>
      <pubDate>Mon, 03 Jul 2023 08:12:00 +0000</pubDate>
      <link>https://dev.to/bjornvdlaan/3-steps-towards-a-greener-it-landscape-15fe</link>
      <guid>https://dev.to/bjornvdlaan/3-steps-towards-a-greener-it-landscape-15fe</guid>
      <description>&lt;p&gt;Businesses all over the world are moving towards a greener future. Steel factories look at green steel, dairy companies offer plant-based alternatives,  office roofs get solar panels, and cloud providers make sustainability pledges. However, even though every company nowadays uses software and hardware of some form, the IT landscape is often not considered in those sustainability plans, even though data centres have substantial carbon emissions (more than aviation and growing!). Sustainability is also not of mind when software and hardware developers talk about non-functional requirements; they think about performance and reliability but not so much about carbon efficiency.&lt;/p&gt;

&lt;p&gt;I believe software engineers have a largely untapped opportunity to contribute to a greener world. And a big bonus: as we green our IT landscape, the applications in it also likely become cheaper to run, with better performance and resilience. Win-win!&lt;/p&gt;

&lt;p&gt;In this article, we will take three steps into the world of green software engineering, with concrete examples along the way. The three steps are visualised below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a8zrYCHe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4uxe4mnkoe9itwn899to.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a8zrYCHe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4uxe4mnkoe9itwn899to.png" alt="Image description" width="598" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Software Application
&lt;/h2&gt;

&lt;p&gt;Running our applications causes carbon to be emitted. We will probably never be able to emit nothing at all, but we can work to minimise the amount of carbon emitted per unit of work. There are essentially two approaches to making applications more energy-efficient.&lt;/p&gt;

&lt;p&gt;The first approach is to simply make our application consume less energy. How to achieve this exactly depends on your application. For web applications, we could look at the network communication between the frontend and the backend and identify three options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reduce the size of network packets by minifying the web assets or removing unused CSS definitions.&lt;/li&gt;
&lt;li&gt;Reduce the distance those packets travel by introducing a CDN (Content Delivery Network).&lt;/li&gt;
&lt;li&gt;Reduce the number of API calls needed to render the page.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We could even look at the programming language you pick. Five years ago, researchers in Portugal compared the energy consumption of several popular programming languages and showed how their execution time and memory usage influenced energy usage. They found that compiled languages (C, Go, Rust, Java) tend to be more energy efficient than interpreted languages (JavaScript, Python).&lt;/p&gt;

&lt;p&gt;Alternatively, we can make our applications more intelligent. Our power grid is supplied with electricity from various sources. When the sun is shining and the wind is blowing, then more of the available energy comes from renewable sources. Based on that concept, we build our applications to be carbon-aware: do more when more energy comes from low-carbon sources and less when more energy comes from high-carbon sources. Imagine an application that reads a task description from Kafka and performs some CPU-intensive computation that adjusts its pace based on how clean the current energy supply is. This pattern is called &lt;strong&gt;demand shaping&lt;/strong&gt;, as we shape the energy demand of our application based on the carbon intensity.&lt;/p&gt;

&lt;p&gt;Instead of changing the pace (demand shaping), we can also shift to a different time and/or place. This is called &lt;strong&gt;demand shifting&lt;/strong&gt;. Imagine that your company is in Australia, and you want to run computations during nighttime. You could run the workload in the Netherlands, where its daytime and where solar power is more readily available.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Runtime Platform
&lt;/h2&gt;

&lt;p&gt;Our software applications are often operated on runtime platforms such as Kubernetes, which makes them a great place to achieve more sustainability gains. Making improvements at the platform-level profits all applications running on it. The k8s community has some great initiatives to assist you. I'll give three examples here.&lt;/p&gt;

&lt;p&gt;Suppose your company has separate Dev, Test, Acceptance and Production environments. In that case, you can already achieve significant emission reduction by turning off the Dev and Test clusters on weekends. You can automate this through &lt;code&gt;kube-green&lt;/code&gt;, which automatically shuts down your deployments and cronjobs when you don't need them. An added benefit: you also promote a healthy working environment where developers take some rest outside business hours.&lt;/p&gt;

&lt;p&gt;By default, Kubernetes scales workloads based on CPU and RAM utilisation. However, we can use components like &lt;code&gt;KEDA&lt;/code&gt; to scale based on relevant demand metrics such as HTTP requests or queue length. This can help us reduce resource utilisation and, therefore, your carbon emissions. &lt;code&gt;KEDA&lt;/code&gt; can even scale deployments down to zero.&lt;/p&gt;

&lt;p&gt;Finally, we can adjust the Scheduler to assign pods to nodes based on the carbon intensity at that machine's location. Bill Johnson, Principal Software Engineering Manager, wrote an article about this topic &lt;a href="https://devblogs.microsoft.com/sustainable-software/carbon-aware-kubernetes/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Hardware
&lt;/h2&gt;

&lt;p&gt;All software we build eventually runs on hardware, from servers in a datacenter to the phones and laptops of our end users. These devices need to be manufactured, and they also need to be dismantled eventually. The carbon that is emitted during the creation and disposal of a device is called &lt;strong&gt;embodied carbon&lt;/strong&gt;. The bar chart below (credits: &lt;a href="https://learn.greensoftware.foundation/hardware-efficiency"&gt;Green Software Foundation&lt;/a&gt;) shows that this embodied carbon is not a trivial factor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RXd82GCN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/61iewm87npjuh0r3wkya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RXd82GCN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/61iewm87npjuh0r3wkya.png" alt="Emissions of end user devices, by Green Software Foundation" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reducing the embodied carbon of servers requires embracing responsible lifecycle management, which involves implementing proper disposal and recycling methods for old servers. By refurbishing, reselling, or recycling them, we extend their useful life and prevent them from ending up in landfills.&lt;/p&gt;

&lt;p&gt;Besides the embodied carbon, we should also consider how servers are used. Simply said: the more we utilise a server, the more efficient it can run our applications. We call this &lt;strong&gt;energy proportionality&lt;/strong&gt; (more info &lt;a href="https://research.google/pubs/pub33387/"&gt;here&lt;/a&gt;). Therefore, we can run our software using less electricity by running on as few servers as possible.&lt;/p&gt;

&lt;p&gt;I believe that embodied carbon and energy proportionality are why cloud providers have an amazing opportunity to contribute to a better world. I am happy to see the first steps already being taken. Servers in data centres are often written off after four; all three big cloud vendors recently announced extending this lifespan in the light of sustainability. Also, as they manage the applications of multiple companies, they can intelligently allocate workloads in such a way that no servers are sitting idle, improving utilisation rates.&lt;/p&gt;

&lt;p&gt;Besides making hardware more sustainable under the cloud-hood, cloud providers should also give tools to their customers to make greener choices. For example, AWS Lambda has a SnapStart option to decrease startup times. In the same way, we could have a CarbonAwareStart that applies demand shaping to Lambda executions. Moreover, cloud providers could inform customers about their emissions in the same way as they now give insight into the costs. What gets measured gets improved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Green software invites us, software engineers, to make our IT landscape more sustainable. Engineers can play a vital role in protecting the environment by focusing on energy efficiency, reducing waste, and minimising carbon emissions. Joining the green software movement is an opportunity and a responsibility to build a better future together. Using sustainable practices, we can drive innovation, improve performance, and contribute to a resilient digital world across all industries. Let's work together to create a greener, more sustainable planet.&lt;/p&gt;

&lt;p&gt;Interested? Check out the &lt;a href="https://learn.greensoftware.foundation/"&gt;Green Software Foundation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Image attributions
&lt;/h2&gt;

&lt;p&gt;Cover photo by Matt Anderson on Unsplash.&lt;/p&gt;

&lt;p&gt;The icons used in my visualisation are found on Flaticon.com.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Globe icons created by Uniconlabs - Flaticon&lt;/li&gt;
&lt;li&gt;Lifespan icons created by Flowicon - Flaticon&lt;/li&gt;
&lt;li&gt;Server icons created by Freepik - Flaticon&lt;/li&gt;
&lt;li&gt;Development icons created by Design Circle - Flaticon&lt;/li&gt;
&lt;li&gt;Application icons created by Smashicons - Flaticon&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>greensoftware</category>
      <category>programming</category>
    </item>
    <item>
      <title>AWS announces AWS CarbonAwareBatch, the Future of Sustainable Cloud Computing</title>
      <dc:creator>BjornvdLaan</dc:creator>
      <pubDate>Thu, 22 Jun 2023 08:24:49 +0000</pubDate>
      <link>https://dev.to/bjornvdlaan/aws-announces-aws-carbonawarebatch-the-future-of-sustainable-cloud-computing-4n5k</link>
      <guid>https://dev.to/bjornvdlaan/aws-announces-aws-carbonawarebatch-the-future-of-sustainable-cloud-computing-4n5k</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HuzZLWVe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4yjawziu3fri0u6tnfj1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HuzZLWVe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4yjawziu3fri0u6tnfj1.png" alt="AWS CarbonAwareBatch" width="480" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Today, Amazon Web Services (AWS) announced the launch of AWS CarbonAwareBatch, a revolutionary new service that reimagines the way companies approach sustainable cloud computing. The solution not only prioritizes computing efficiency but also harnesses the power of environmental responsibility.&lt;/p&gt;

&lt;p&gt;AWS CarbonAwareBatch, built on the robust framework of AWS Batch, is designed to enhance businesses' green credentials by intelligently scheduling batch jobs during periods of low-carbon intensity. It enables organizations to minimize the carbon footprint associated with their computing tasks, bringing a new level of environmental consciousness to the technology sector.&lt;/p&gt;

&lt;p&gt;"Efficiency and innovation are key pillars at AWS. With CarbonAwareBatch, we're not only redefining these terms but also factoring in sustainability," said April McGreen, Tech Lead of AWS CarbonAwareBatch. "We are empowering our customers to make positive environmental choices, seamlessly aligning their technological needs with their environmental goals."&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;p&gt;In addition to harnessing low-carbon periods, AWS CarbonAwareBatch boasts a range of innovative features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Carbon Footprint Dashboard&lt;/strong&gt;: An interactive, user-friendly dashboard provides real-time data on the carbon emissions saved by utilizing the service. This provides businesses with tangible data to showcase their commitment to sustainability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Carbon Credits:&lt;/strong&gt; AWS CarbonAwareBatch calculates the carbon emissions saved and converts them into carbon credits, which businesses can use towards their sustainability goals or trade in carbon credit markets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sustainability Scorecard:&lt;/strong&gt; This feature gives an in-depth analysis of a company's sustainable usage patterns and offers recommendations on how to optimize for further environmental impact reduction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Machine Learning Optimizations:&lt;/strong&gt; Leveraging machine learning, AWS CarbonAwareBatch forecasts power grid carbon intensity, allowing it to optimally schedule jobs to further minimize carbon emissions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By leveraging AWS CarbonAwareBatch, businesses can now align their operations with the principles of a green economy, reducing their carbon footprint without compromising on efficiency or productivity.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future is Green
&lt;/h2&gt;

&lt;p&gt;With AWS CarbonAwareBatch, Amazon Web Services continues to demonstrate its commitment to driving sustainable innovation. By integrating environmental consciousness into its core services, AWS is not just preparing for the future, but actively shaping it, ensuring that technology continues to serve as a force for good.&lt;/p&gt;

&lt;p&gt;The future of cloud computing is not just about convenience and flexibility - it's also about stewardship of our planet. AWS CarbonAwareBatch is the vanguard of this new era, providing the tools businesses need to be at the forefront of the green revolution.&lt;/p&gt;

&lt;p&gt;…&lt;/p&gt;

&lt;p&gt;That would have been cool, right?&lt;/p&gt;

&lt;p&gt;While this announcement and the service of AWS CarbonAwareBatch sounds like the perfect blend of technological innovation and environmental responsibility, this announcement is unfortunately not real. It's a projection of what could be possible in the future, generated by ChatGPT and myself.&lt;/p&gt;

&lt;p&gt;As we continue to grapple with the realities of climate change, we hope to see companies like AWS pioneering more solutions like this - technologies that not only propel us forward but also protect and preserve our world.&lt;/p&gt;

&lt;p&gt;We look forward to a day when AWS CarbonAwareBatch, or a service very much like it, moves from the realm of AI-generated possibilities into tangible reality. Until then, we encourage everyone - technologists, businesses, and consumers alike - to continue pushing for a more sustainable future.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>greensoftware</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Kotlin Multiplatform’s three levels of testing with Kotest</title>
      <dc:creator>BjornvdLaan</dc:creator>
      <pubDate>Sat, 21 Jan 2023 15:48:19 +0000</pubDate>
      <link>https://dev.to/bjornvdlaan/kotlin-multiplatforms-three-levels-of-testing-with-kotest-3e24</link>
      <guid>https://dev.to/bjornvdlaan/kotlin-multiplatforms-three-levels-of-testing-with-kotest-3e24</guid>
      <description>&lt;p&gt;Kotlin Multiplatform allows us to share common Kotlin code between various platforms, including JVM, JavaScript, iOS, Android, and native. This is useful when we have shared business logic relevant to all platforms. If functions in that common codebase require a platform-specific implementation, then Kotlin Multiplatform offers a mechanism of expected and actual declarations to essentially create placeholders that are implemented per platform. We examine this mechanism later in this article. A Kotlin Multiplatform project contains multiple modules called source sets to enable such code sharing. By default, each platform has two source sets, one for the code (jvmMain, jsMain) and one for the tests (jvmTest, jsTest) and additionally two source sets for common code (commonMain and commonTest) that are used in all main compilations of a project. These modules are structured in a hierarchy to provide any level of code sharing.&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%2F4yorek6uysg4py2il2rm.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%2F4yorek6uysg4py2il2rm.png" width="571" height="471"&gt;&lt;/a&gt;&lt;br&gt;Source set hierarchy in Kotlin Multiplatform.
  &lt;/p&gt;

&lt;p&gt;One use case is to share dependencies. Below we see an example where commonTest defines Kotest dependencies used for all platforms, and the JVM-specific dependencies are added to the jvmTest source set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;sourceSets&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;`commonTest`&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;getting&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kotest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kotest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;framework&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test-common"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test-annotations-common"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;jvmTest&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;getting&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kotest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;junit5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each platform also has its own specific code so that, for instance, the JVM part of your application can implement a server, the front-end is built in JavaScript code, and the mobile apps are created based on the source sets for Android and iOS.&lt;/p&gt;

&lt;p&gt;This article shows how we can test all three layers of code in a Kotlin Multiplatform project: common, expected / actual declaration, and platform-specific code. We use Kotest as our testing framework because it has built-in multiplatform support. The framework delegates testing to platform-specific engines, such as JUnit and Mocha. All code samples can be found on GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common code
&lt;/h2&gt;

&lt;p&gt;Code that is shared by all platforms by default resides in the commonMain source set. It wouldn’t make sense to write tests for it in every platform, so instead, we can use the commonTest source set to store the common test suite. Let’s say that we need a function that calculates the factorial of a number in source set commonMain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;nl.bjornvanderlaan.factorial&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;
    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can provide the test code in &lt;code&gt;commonTest&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;nl.bjornvanderlaan.factorial&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.kotest.core.spec.style.StringSpec&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.kotest.matchers.ints.shouldBeExactly&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FactorialTest&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StringSpec&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="s"&gt;"Factorial of 5 is 120"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;shouldBeExactly&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, testing common code works the same as in ‘normal’ Kotlin projects. The cool part however is that this function can now be used in all possible platforms, which can be a huge bonus if we write all the business logic here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expected / Actual declarations
&lt;/h2&gt;

&lt;p&gt;Sometimes, there are functions that we want to use in our common code, but they can be implemented much more efficiently with platform-specific code. For instance, Kotlin’s multiplatform standard library does not provide a way to encode a string in base64. We could roll our own implementation or look for an open-source library, but some of our target platforms already provide this functionality out of the box. Kotlin provides a mechanism of expected and actual declarations to tackle this situation. With this mechanism, a common source set defines an expected declaration, and platform-specific source sets provide the actual declaration corresponding to the expected declaration and the actual implementation. Besides functions, this mechanism also works for other constructs such as classes, enums, and annotations. Below we see an example of this mechanism for the JVM. The code is based on a tutorial from JetBrains.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Source set: commonMain
 */&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;nl.bjornvanderlaan.base64&lt;/span&gt;

&lt;span class="n"&gt;expect&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;encodeBase64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;encodeGreeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Greetings $name, from Kotlin Multiplatform!"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Source set: jvmMain
 */&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;nl.bjornvanderlaan.base64&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.Base64&lt;/span&gt;

&lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;encodeBase64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEncoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encodeToByteArray&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;decodeToString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, the expected function in commonMain can be used by other functions such as encodeGreeting. Through this mechanism, we can write common code where it makes sense and let leave placeholders where we expect the platforms to deliver an actual implementation. Similar to the common code, we can write our test in commonTest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Source set: commonTest
 */&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base64Test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StringSpec&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="s"&gt;"String is properly encoded to Base64"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;input&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Kotlin is awesome"&lt;/span&gt;

        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;encodedInput&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;encodeBase64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;encodedInput&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="s"&gt;"S290bGluIGlzIGF3ZXNvbWU="&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we let Gradle test our project, it automatically executes this test for all platform-specific variants of the expected function. So even though we have multiple actual implementations, we do not need to repeat our test suite.&lt;/p&gt;

&lt;h2&gt;
  
  
  Specific Function
&lt;/h2&gt;

&lt;p&gt;Finally, there are parts of the application that we want to build for specific platforms only. Because source sets are organised hierarchically, we can choose which test source we want to put our tests in. When we make a web application, we want the server code to be written specifically for the JVM, and the JavaScript source set would contain the front-end code. The tests are then likely put in jvmTest and jsTest source sets, respectively. We can use Kotest to write tests like we would for single-platform Kotlin projects. The advantage of Kotest is that we have one unified testing framework, and we can also use all helper functions and custom assertions from parent source sets. Let’s say that we want to write a function for the JVM platform that calculates the factorial of a factorial of a number. Note that the value is already bigger than what Integer can handle if the number is 4. Luckily, as this function is specific to the JVM, we can use BigInteger for this purpose.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Source set: jvmMain
 */&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;nl.bjornvanderlaan.factorialoffactorial&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;nl.bjornvanderlaan.factorial.factorial&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.math.BigInteger&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;factorialOfFactorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;BigInteger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;bigNumber&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBigInteger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;currentNumber&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BigInteger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ONE&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;accumulator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BigInteger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ONE&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentNumber&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;bigNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;accumulator&lt;/span&gt; &lt;span class="p"&gt;*=&lt;/span&gt; &lt;span class="n"&gt;currentNumber&lt;/span&gt;
        &lt;span class="n"&gt;currentNumber&lt;/span&gt;&lt;span class="p"&gt;++&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;accumulator&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
kotlin&lt;/p&gt;

&lt;p&gt;The function above also shows how the jvmMain source set can use the factorial function from its parent commonMain in the same way it can use its own functions. The function above can be tested with Kotest in the same way as we have seen in the other examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Source set: jvmTest
 */&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;nl.bjornvanderlaan.factorialoffactorial&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.kotest.core.spec.style.StringSpec&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.kotest.matchers.shouldBe&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.math.BigInteger&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FactorialOfFactorialTest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StringSpec&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="s"&gt;"Factorial of factorial of 4 is 620448401733239439360000"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;factorialOfFactorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="nc"&gt;BigInteger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"620448401733239439360000"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function and its tests are defined in the JVM-specific part of our application and can, therefore, benefits from classes like BigInteger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intermediate levels
&lt;/h2&gt;

&lt;p&gt;The examples above assume only two levels of hierarchy: the common parent and its platform-specific children. In practice, we might introduce more levels. For instance, if we also build a mobile application for iOS and Android, we might have an intermediate source sets mobileMain that depends on the commonMain source set and acts as the parent for iosTest and androidMain. In that case, the mobileTest source set can contain tests for functions implemented in mobileMain or expected functions implemented in child source sets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Kotlin Multiplatform helps us reduce the time spent on writing and maintaining the same code for different platforms, and Kotest helps us to unify our testing efforts by providing a multiplatform framework. Multiplatform projects are structured as a hierarchy of source sets. The rules for testing are the same at every node: Kotest executes our tests on either the implementation in the main source set itself or fetches the actual implementations from child source sets. As such, Kotlin Multiplatform offers us the flexibility to create a super DRY codebase and test suite, regardless of the number of platforms we want to support.&lt;/p&gt;

</description>
      <category>welcome</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Property-based testing with Kotlin and Kotest: the Christmas edition</title>
      <dc:creator>BjornvdLaan</dc:creator>
      <pubDate>Sat, 21 Jan 2023 15:38:16 +0000</pubDate>
      <link>https://dev.to/bjornvdlaan/property-based-testing-with-kotlin-and-kotest-the-christmas-edition-192n</link>
      <guid>https://dev.to/bjornvdlaan/property-based-testing-with-kotlin-and-kotest-the-christmas-edition-192n</guid>
      <description>&lt;p&gt;This article was previously posted as part of the &lt;a href="https://www.javaadvent.com/2022/12/property-based-testing-with-kotlin-and-kotest.html."&gt;2022 Java Advent calendar&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Christmas is coming, and Santa and the elves are working around the clock to build all toys. After a long day in the toy workshop, the elves want to spend their hard-earned NorthPoleCoins on jingle juice and sweets at the local pub. They always go in groups and often order collectively. They use a special application to keep track of payments so that every elf pays its fair share. We see the service that settles these payments below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oscnC36s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a2temntdbun83q5ns96n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oscnC36s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a2temntdbun83q5ns96n.png" alt="Image description" width="880" height="565"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To preserve peace on the north pole, we want to add some tests to ensure that this service works as intended. One crucial property of the method above is that, at any point in time, the balances of all elves add up to zero. Otherwise, coins would be created or lost. A unit test for this method could look something like the one below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZCXzRE16--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/47mxk94vl8vm0o11o33o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZCXzRE16--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/47mxk94vl8vm0o11o33o.png" alt="Image description" width="880" height="744"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, the scenario is set up by initialising the objects with example values. Then, we invoke the method under test and write assertions to verify that the output is as expected. Of course, it doesn’t stop with this single test case. We generally write additional, similar tests to sufficiently cover all scenarios and edge cases. And when we see that all the test scenarios we came up with are passing, we can relievedly commit or deploy our code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Tkog4a72--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dvsmg6st62a1qvn4qcie.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Tkog4a72--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dvsmg6st62a1qvn4qcie.png" alt="Image description" width="720" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Downsides of example-based testing
&lt;/h2&gt;

&lt;p&gt;If we take a step back, we see that many of these test cases are quite similar, except that each uses a slightly different example. There are a few downsides to this approach. First of all, developers need to spend more time developing and maintaining these test cases. This includes coming up with realistic examples, which requires a thorough understanding of the domain. New developers on the team might also have a hard time grasping the actual intent of the test. It takes time to see what angles are covered for each method and what is missing or redundant. And despite all this effort from the team, only a small portion of all possible examples are covered by the tests they wrote. Covering all combinations of inputs is just not feasible. And even worse, the scenarios covered are the same every run: essentially coupling between scenario and test suite.&lt;/p&gt;

&lt;p&gt;When we implement our methods, we want the logic to be generic and not tied to specific cases. The code should have certain properties for all possible inputs. The tests we write for it are more the opposite: we write tests that are example-specific and not generic. The code and tests are designed on different levels of abstraction, where properties are more abstract than concrete examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Property-based testing to the rescue
&lt;/h2&gt;

&lt;p&gt;But what if we could write tests like we write our code? Property-based testing (short: PBT) can help us out here. Instead of defining the concrete examples to test, you specify the domain (strings, positive integers, etc.) and let the framework generate many input values, including edge cases. Each test is executed multiple times with different possible inputs during a test run. Property-based testing allows us to write one test that tests all of the desired inputs, including typical edge cases, which allows for more efficient test writing and ensures that a test passes for all possible values within the domain.&lt;/p&gt;

&lt;p&gt;Below we see how our test could look as a property-based test. The scenario function is replaced by Kotest’s checkAll, taking as its inputs a set of generators. The class containing all generators is called Arb, which is short for arbitrary. A predefined number of examples is generated for each test run, a thousand by default in Kotest. The rest of the test remains completely the same, except that we added some code to transform the generated values to the entities and values of our domain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--56BbQgrA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1mhj9lcugrlgt0ys87oj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--56BbQgrA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1mhj9lcugrlgt0ys87oj.png" alt="Image description" width="880" height="816"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our example-based tests previously all passed, which might lead us to believe that this property-based test will also be happy. Our test cases represented all possible paths, right? However, we are actually met with a failure this time. You might think that a property-based test is more difficult to debug because of its generated input values, but we did not talk about PBT’s superpower. Kotest not just mentions the failing example if the test fails, it starts ‘shrinking’ the input values. If the test fails with integer value 8 as input then this value is shrunk to value 7, and the test is retried. If the test failed when we tried to input a list of three strings integer then the list is shrunk to two strings. Shrinking continues until the test passes again, and the goal is to find the minimal reproducible example for which the test fails, which helps find the bug. Let’s have a look at the stack trace of our run.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6WmXs-AF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/msbmqywgpxes6fov2evi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6WmXs-AF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/msbmqywgpxes6fov2evi.png" alt="Image description" width="880" height="724"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The test failed when we tried to divide (a rounded) 77.93 NorthPoleCoins over four elves, and Kotest started shrinking both inputs. Interestingly, both shrinks uncovered a different bug for us. Dividing the coins led to a rounding error, while an empty list caused an exception to be thrown. We might not have thought of it when we created our examples, but now we can adapt our code based on these results.&lt;/p&gt;

&lt;p&gt;When we make ourselves as developers responsible for coming up with realistic examples, we risk such bugs slipping through the cracks. Property-based tests make sure that all scenarios can be verified.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom generators
&lt;/h2&gt;

&lt;p&gt;The property-based test we saw above is less readable than we would like because of the logic we added to transform the values from the built-in Arb generators to be suitable for our test. Kotest allows us to define custom Arb’s through the arbitrary builder, and we can use Kotlin’s extension functions to use these custom generators in the same way as their built-in counterparts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qZvcBbeo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ei69f57yt5g379zbzehq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qZvcBbeo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ei69f57yt5g379zbzehq.png" alt="Image description" width="880" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where Kotest knows how to shrink built-in generators, the shrinking behaviour for custom generators can be very dependent on your domain. Therefore, the arbitrary builder allows us to provide a shrink function that returns a list of values we deem to be a shrink result of the current generated value. Let’s have a look at what this could look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6Uu-DSk7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bf6rzh3y9gtd4du74rwu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6Uu-DSk7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bf6rzh3y9gtd4du74rwu.png" alt="Image description" width="880" height="592"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We ended up with a more concise test than the example-based test that we started with, but it covered many more examples than before.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_Nw2tJST--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b46bphcmstmj1f4cst29.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_Nw2tJST--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b46bphcmstmj1f4cst29.png" alt="Image description" width="880" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we’ve seen what property-based testing is and how it differs from the more common example-based testing. Where defining representative examples in example-based testing is the programmer’s responsibility, property-based testing leaves this to generators so that the tests do not become partly dependent on the chosen examples. Property-based testing allows us to generalize concrete examples to focus on the characteristics of the code that underlie these examples, resulting in a cleaner and more compact test suite that is easy to maintain and better exposes subtle bugs. In this way, we bring what we test closer to what we claim to test. However, it is also important to note that property-based testing is not intended to replace example-based unit tests completely. It’s an addition to our testing arsenal that allows us to cover every possible input value with a single test to reveal bugs we might not come up with ourselves.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>testing</category>
      <category>programming</category>
      <category>multiplatform</category>
    </item>
    <item>
      <title>Generate class diagrams with a Kotlin DSL for PlantUML</title>
      <dc:creator>BjornvdLaan</dc:creator>
      <pubDate>Sat, 18 Dec 2021 15:05:27 +0000</pubDate>
      <link>https://dev.to/bjornvdlaan/generate-class-diagrams-with-a-kotlin-dsl-for-plantuml-4bi9</link>
      <guid>https://dev.to/bjornvdlaan/generate-class-diagrams-with-a-kotlin-dsl-for-plantuml-4bi9</guid>
      <description>&lt;p&gt;Technical documentation of the systems helps us and our (future) colleagues to understand how things are built and how they work. The importance of internal documentation was recently also proven by the &lt;a href="https://cloud.google.com/blog/products/devops-sre/announcing-dora-2021-accelerate-state-of-devops-report"&gt;State of DevOps 2021&lt;/a&gt;, which notes it as one of its key findings this year. However, while probably most of us understand the relevance of good documentation, it is still not trivial to always keep it in sync with reality. Deadlines are tight, people want value delivered asap, "we will update it next sprint", and more decisions to get short term gains.&lt;/p&gt;

&lt;p&gt;In a recent project, we therefore wanted to automate the creation of certain parts of the documentation. The first automation candidate we identified were class diagrams. As part of our documentation-as-code approach, these diagrams were already manually made with &lt;a href="https://plantuml.com/"&gt;PlantUML&lt;/a&gt;, an open-source tool allowing users to create diagrams from a plain text language. For example, with PlantUML, we can write the following text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@startuml
class Person {
  + id : Int
  + name : String
  + getName() : String
  + getPets() : List&amp;lt;Pet&amp;gt;
}

class Pet {
  + id : Int
  + name : String
  + owner : Person
  + getName() : String
  + getOwner() : Person
}

Person - Pet : owns &amp;gt;
@enduml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The PlantUML tool can take this simple text document and generate a PNG with a cool retro-ish look:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BheQ2K8C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gswfdnwtog2utg1diu23.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BheQ2K8C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gswfdnwtog2utg1diu23.png" alt="Image description" width="369" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With PlantUML, you can update the documentation in the same pull request as your code changes and benefit from git's version-control. Also, as its part of the pull requests, colleagues can actually review the diagrams and so each change to documentation is approved by someone else. I personally think this is already a big improvement compared to stashing your documentation somewhere far away from your code on a Confluence page or, even worse, a Sharepoint.&lt;/p&gt;

&lt;p&gt;For the project that I worked on, we would have these PlantUML diagrams in the repository and a &lt;code&gt;git push&lt;/code&gt; would trigger a pipeline that creates the PNG images and deploys everything to our &lt;a href="https://www.sphinx-doc.org/en/master/"&gt;Sphinx&lt;/a&gt;-based company documentation server. That works well, but we still needed to manually update these diagrams and that felt like applying our changes twice. Once to the code and once to the documentation. Could we not just generate the docs?&lt;/p&gt;

&lt;h1&gt;
  
  
  The plan
&lt;/h1&gt;

&lt;p&gt;We want to create class diagrams automatically. A first thought would be to use StringBuilder. While that is perfectly possible, we preferably had a more structured way that also ensures a checked and standardised output. To this end, we explored creating a Kotlin DSL (Domain-Specific Language) for PlantUML diagrams. This article explains our approach and the decisions we took. Below you see a sneak-peak of the syntax. Do you like what you see? Then please read on!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;myDiagram&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;classDiagram&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Person"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Integer"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"getPets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"List&amp;lt;Pet&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pet"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Integer"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"getName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"String"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"getOwner"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Person"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"String"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;relationship&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Person"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Pet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RelationshipType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ASSOCIATION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"is owned by"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Lambda with receivers
&lt;/h1&gt;

&lt;p&gt;The secret ingredient to create DSL's in Kotlin is its receiver types. Using these, a lambda with a receiver allows you to call methods of an object in the body of a lambda without any qualifiers. Much has already been written about receivers in other articles so, in this article, we will only look at the applying lambda's with receivers to our specific use case.&lt;/p&gt;

&lt;h1&gt;
  
  
  Diagram-as-a-constrained-tree
&lt;/h1&gt;

&lt;p&gt;The key idea behind our PlantUML DSL is to see a class diagram as a very constrained tree structure. Each node has children of a very specific type. For example, a class can only have fields and methods.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s6z2Sjxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q9fzmaz2tagi85gvu69y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s6z2Sjxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q9fzmaz2tagi85gvu69y.png" alt="Image description" width="492" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DSL's are great to encode such constraints. Let's start by defining an abstract base class for all our elements. &lt;code&gt;PlantUmlElement&lt;/code&gt; has a list to keep track of its children which are all subtypes of &lt;code&gt;PlantUmlElement&lt;/code&gt; as well. A new child can be added through the &lt;code&gt;addChild&lt;/code&gt; method, which has two implementations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The first implementation accepts just the new child as parameter. This method will be used if that child is a leaf node and does not require further initialisation.&lt;/li&gt;
&lt;li&gt;Otherwise, we also pass a lambda with receiver to initialise the grandchildren. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We also require subclasses to implement the &lt;code&gt;toString()&lt;/code&gt; method to get the PlantUML String. I admit that this is a slight misuse of &lt;code&gt;toString()&lt;/code&gt;, but it does make our syntax cleaner for our example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlantUmlElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;children&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mutableListOf&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PlantUmlElement&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PlantUmlElement&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;addChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PlantUmlElement&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;addChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Implementation of the elements
&lt;/h1&gt;

&lt;p&gt;All elements essentially follow the same approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Extend the &lt;code&gt;PlantUmlElement&lt;/code&gt; base class.&lt;/li&gt;
&lt;li&gt;If the element is not a leaf, then expose a method for each possible child element type that calls &lt;code&gt;addChild&lt;/code&gt;. If the child element type is also not a leaf, then the variant of &lt;code&gt;addChild&lt;/code&gt; is called with a lambda with receiver. &lt;/li&gt;
&lt;li&gt;Override &lt;code&gt;toString()&lt;/code&gt; to return the PlantUML representation of this element. If the element is not a leaf, then call the &lt;code&gt;toString()&lt;/code&gt; methods of the children as well.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The implementation then looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AttributeVisibility&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;PUBLIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PRIVATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PROTECTED&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;second&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;PlantUmlElement&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AttributeVisibility&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AttributeVisibility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PUBLIC&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;setVisibility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AttributeVisibility&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Attribute&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visibility&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;visibility&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getModifier&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;AttributeVisibility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PUBLIC&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"+"&lt;/span&gt;
            &lt;span class="nc"&gt;AttributeVisibility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PRIVATE&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"-"&lt;/span&gt;
            &lt;span class="nc"&gt;AttributeVisibility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PROTECTED&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"*"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;dataType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dataType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"${getModifier()} $name ${if(dataType != null) "&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;dataType&lt;/span&gt;&lt;span class="s"&gt;" else ""}"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;returnType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;returnType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"${getModifier()} $name() ${if(returnType != null) "&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;returnType&lt;/span&gt;&lt;span class="s"&gt;" else ""}"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RelationshipType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;EXTENSION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;COMPOSITION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;AGGREGATION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ASSOCIATION&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Relationship&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;RelationshipType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;PlantUmlElement&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"$left ${getArrow()} $right ${if (label != null) "&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="s"&gt;" else ""}"&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getArrow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;RelationshipType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;EXTENSION&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;|--"&lt;/span&gt;
            &lt;span class="nc"&gt;RelationshipType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;COMPOSITION&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"*--"&lt;/span&gt;
            &lt;span class="nc"&gt;RelationshipType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AGGREGATION&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"o--"&lt;/span&gt;
            &lt;span class="nc"&gt;RelationshipType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ASSOCIATION&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;--"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;PlantUmlElement&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;addAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Attribute&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="n"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AttributeVisibility&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;addChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setVisibility&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;public&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;vararg&lt;/span&gt; &lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Attribute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;addAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;AttributeVisibility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PUBLIC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;private&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;vararg&lt;/span&gt; &lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Attribute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;addAttributes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;AttributeVisibility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PRIVATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"class \"${this.name}\" as ${this.identifier} {\n\t${children.joinToString("&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="s"&gt;")}\n}"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ClassDiagram&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PlantUmlElement&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;.()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="nf"&gt;addChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;relationship&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;RelationshipType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="nf"&gt;addChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Relationship&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"@startuml ldm\n\t${children.joinToString("&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="s"&gt;")}\n@enduml"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All that we now still require is a function to start with the root &lt;code&gt;ClassDiagram&lt;/code&gt;. This function is not part of a class and accepts a lambda with receiver that is then applied to a freshly created &lt;code&gt;ClassDiagram&lt;/code&gt; object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;classDiagram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ClassDiagram&lt;/span&gt;&lt;span class="p"&gt;.()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ClassDiagram&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  A more complex example
&lt;/h1&gt;

&lt;p&gt;Using the DSL we just created, we can make class diagrams that are slightly more complex than what we already saw in this article. For example, we can write the following diagram:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;myDiagram&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;classDiagram&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Person"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Integer"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"getName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"String"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Customer"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Integer"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"emailAddress"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"String"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"getName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"String"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"getAccountBalance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Integer"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"verifyPassword"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Boolean"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"String"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"String"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Wallet"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Integer"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"customerId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Integer"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"getBalance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Integer"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Integer"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"customerId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Integer"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"place"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Integer"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"getTotalPrice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Float"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;clazz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"OrderItem"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Integer"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"orderId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Integer"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"getDescription"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"String"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"String"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Float"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;relationship&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Person"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Customer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RelationshipType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;EXTENSION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;relationship&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Customer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Wallet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RelationshipType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AGGREGATION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"belongs to"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nf"&gt;relationship&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"OrderItem"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RelationshipType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;COMPOSITION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"is part of"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nf"&gt;relationship&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Customer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RelationshipType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ASSOCIATION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"places"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myDiagram&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which results in this image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8Ue965ch--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9lc10p4htw78lox71fwo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8Ue965ch--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9lc10p4htw78lox71fwo.png" alt="Image description" width="392" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Et Voila!
&lt;/h1&gt;

&lt;p&gt;That's all we need to create a simple class diagram. Obviously, PlantUML has a lot more features and these can be integrated into our DSL as well. To actually automate documentation generation, we would then use &lt;code&gt;forEach&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt; and other language features to dynamically put such diagram together. At least, we now have an approach that is more robust that just building a string using &lt;code&gt;StringBuilder&lt;/code&gt; or just appending Strings. We can enforce constraints and the generation code is also much more readable.&lt;/p&gt;

&lt;p&gt;The full code and all examples in this article can be found on GitHub: &lt;a href="https://github.com/BjornvdLaan/plantuml-dsl-kotlin"&gt;BjornvdLaan/plantuml-dsl-kotlin&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>documentation</category>
      <category>plantuml</category>
      <category>dsl</category>
    </item>
    <item>
      <title>Create your REST API using OpenAPI kotlin-spring generator</title>
      <dc:creator>BjornvdLaan</dc:creator>
      <pubDate>Thu, 11 Nov 2021 12:04:09 +0000</pubDate>
      <link>https://dev.to/bjornvdlaan/create-your-rest-api-using-openapi-kotlin-spring-generator-5154</link>
      <guid>https://dev.to/bjornvdlaan/create-your-rest-api-using-openapi-kotlin-spring-generator-5154</guid>
      <description>&lt;p&gt;When building an API, I like to start by first creating a formal definition before starting the actual development. One of the most established standards for such API definitions is OpenAPI (formerly Swagger). One pleasant aspect of starting with a definition is that, once the definition is created and agreed upon, we can generate part of our codebase using the OpenAPI Generator. As the name suggests, the OpenAPI Generator generates code from an OpenAPI specification. More specifically, the generator is able to create code for client libraries, server stubs, documentation, and configuration in many different languages and frameworks.&lt;/p&gt;

&lt;p&gt;I know this generator from my Java projects and decided to use it with Kotlin as well. Lucky for me, there is actually a generator for Spring Boot-based Kotlin projects! I, however, could not find a tutorial dedicated to it and therefore I share my steps here in this tutorial.&lt;/p&gt;

&lt;h1&gt;
  
  
  TL;DR
&lt;/h1&gt;

&lt;p&gt;You can find the full project (including &lt;code&gt;target&lt;/code&gt;) at &lt;a href="https://github.com/BjornvdLaan/kotlin-spring-boot-open-api-example" rel="noopener noreferrer"&gt;https://github.com/BjornvdLaan/kotlin-spring-boot-open-api-example&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  What we want to achieve
&lt;/h1&gt;

&lt;p&gt;We wish to generate the server stub using the OpenAPI Generator via its Maven plugin. An advantage of using the Maven (or Gradle) plugin is that the generation becomes part of our API’s build lifecycle and the generated code is always in sync with your OpenAPI definition when you clean compile your code.&lt;/p&gt;

&lt;p&gt;For our example in this tutorial, we will use the &lt;a href=""&gt;PetStore OpenAPI specification&lt;/a&gt; from &lt;a href=""&gt;this tutorial&lt;/a&gt; on Baeldung. After running the Maven plugin, the generated code is found in the &lt;code&gt;target/generated-sources/openapi&lt;/code&gt; directory (see below). After generation, we ‘only’ still need to implement the generated Delegate interfaces within our own &lt;code&gt;src&lt;/code&gt; directory and, voila, the API is implemented. We will implement one of the routes in the PetStore specification and test it using curl.&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%2Fz2ggryipqdjmjpkdhwpi.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%2Fz2ggryipqdjmjpkdhwpi.png" alt="Generated sources directory"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Create your Spring Boot project
&lt;/h1&gt;

&lt;p&gt;We start by creating a fresh Spring Boot project using the &lt;a href="https://start.spring.io/" rel="noopener noreferrer"&gt;Spring Initializr&lt;/a&gt;. We tell it to be a Maven project using Kotlin and packaged as a jar. We also add the Spring Web dependency because the generated code depends on this dependency.&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%2Fdwmw5n0dvdt05bg91toy.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%2Fdwmw5n0dvdt05bg91toy.png" alt="Spring Initializr"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After downloading the project, we open it in our favorite IDE and we add the &lt;code&gt;petstore.yml&lt;/code&gt; OpenAPI definition to &lt;code&gt;src/main/resources&lt;/code&gt; to end up with the following project structure.&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%2F9tm6ld71tq4281pqcavj.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%2F9tm6ld71tq4281pqcavj.png" alt="Project structure before generation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before we can start, we need to add one more dependency to our &lt;code&gt;pom.xml&lt;/code&gt; that the generated code depends on:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;             
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-validation&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h1&gt;
  
  
  Add the Maven plugin
&lt;/h1&gt;

&lt;p&gt;Now it's time to add the OpenAPI Generator's Maven plugin to &lt;code&gt;pom.xml&lt;/code&gt;. &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.openapitools&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;openapi-generator-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.1.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;generate&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;inputSpec&amp;gt;&lt;/span&gt;
                    ${project.basedir}/src/main/resources/petstore.yml
                &lt;span class="nt"&gt;&amp;lt;/inputSpec&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;generatorName&amp;gt;&lt;/span&gt;kotlin-spring&lt;span class="nt"&gt;&amp;lt;/generatorName&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;configOptions&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;basePackage&amp;gt;&lt;/span&gt;nl.bjornvanderlaan.petstoreapi&lt;span class="nt"&gt;&amp;lt;/basePackage&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;apiPackage&amp;gt;&lt;/span&gt;nl.bjornvanderlaan.petstoreapi.api&lt;span class="nt"&gt;&amp;lt;/apiPackage&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;modelPackage&amp;gt;&lt;/span&gt;nl.bjornvanderlaan.petstoreapi.model&lt;span class="nt"&gt;&amp;lt;/modelPackage&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;configPackage&amp;gt;&lt;/span&gt;nl.bjornvanderlaan.petstoreapi.config&lt;span class="nt"&gt;&amp;lt;/configPackage&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;delegatePattern&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/delegatePattern&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;interfaceOnly&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/interfaceOnly&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;modelNameSuffix&amp;gt;&lt;/span&gt;Dto&lt;span class="nt"&gt;&amp;lt;/modelNameSuffix&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/configOptions&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;An overview of all configuration options is found &lt;a href="https://github.com/OpenAPITools/openapi-generator/blob/master/docs/generators/kotlin-spring.md" rel="noopener noreferrer"&gt;here&lt;/a&gt;. I would like to highlight three options where I made a design decision:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;[delegatePattern = true]&lt;/strong&gt; - I asked the generator to use the Delegate pattern so the routes and their logic are separated. The Delegate implementations that we write ourselves can be injected in the generated code, without any need to manually touch the generated sources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[interfaceOnly = false]&lt;/strong&gt; - This option determines whether the controller implementation will also be generated. This controller implementation essentially just autowires the relevant Delegate to be used in the routes. I feel its good to let it be generated because then no engineer can be tempted to add other functionality to the controller, maintaining better separation of concerns. If you really want to do a lot more in your controller then it is worth considering to set this option to &lt;strong&gt;true&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[modelNameSuffix = Dto]&lt;/strong&gt; - I prefer to use the Dto (Data Transfer Object) pattern to separate these objects from the entities that I will use in the service layer. As such, I add a suffix to make the pattern usage explicit.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Generate the code
&lt;/h1&gt;

&lt;p&gt;We can generate the code by running &lt;code&gt;mvn clean generate-sources&lt;/code&gt; and we then obtain the following project structure:&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%2Fj2uk4o1hvm3f22qn3jrb.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%2Fj2uk4o1hvm3f22qn3jrb.png" alt="Project structure after generation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For IntelliJ users: the &lt;code&gt;target/generated-sources/openapi/src/main/kotlin&lt;/code&gt; folder can be marked as an generated sources root. Right-click this folder, then go to 'Mark Directory as' and select Generated Sources Root.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;target/generated-sources/openapi/src/main/kotlin&lt;/code&gt; now contains two packages with a couple of classes each. Let's quickly go through them one by one.&lt;/p&gt;

&lt;h2&gt;
  
  
  api.PetsApi.kt
&lt;/h2&gt;

&lt;p&gt;This interface class defines one method for each API route from our specification using Spring Web annotations like &lt;code&gt;@RequestMapping&lt;/code&gt;, &lt;code&gt;@GetMapping&lt;/code&gt;, and &lt;code&gt;@PostMapping&lt;/code&gt;.&lt;br&gt;
The methods are all one-liners as they simply delegate the work to the Delegate, exactly as we configured.&lt;br&gt;
The interface also has an unimplemented &lt;code&gt;getDelegate()&lt;/code&gt; which will be implemented by &lt;code&gt;PetsApiController&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  api.PetsApiDelegate
&lt;/h2&gt;

&lt;p&gt;This interface defines the methods used by the &lt;code&gt;PetsApi&lt;/code&gt; with a standard implementation that just returns status 501 Not Implemented. We will implement the interface in our &lt;code&gt;src&lt;/code&gt; folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  api.PetsApiController.kt
&lt;/h2&gt;

&lt;p&gt;The Controller implements the PetsApi interface by autowiring the PetsApiDelegate implementation. As mentioned, I configured OpenAPI Generator to generate this class for me because I feel this is all the Controller should do.&lt;/p&gt;

&lt;h2&gt;
  
  
  api.Exceptions
&lt;/h2&gt;

&lt;p&gt;This &lt;code&gt;@ControllerAdvice&lt;/code&gt; class defines default exception handling for three exceptions that are commonly encountered.&lt;/p&gt;

&lt;h2&gt;
  
  
  api.ApiUtils
&lt;/h2&gt;

&lt;p&gt;This object (singleton) defines a utility method used by a utility method for GET mappings.&lt;/p&gt;

&lt;h2&gt;
  
  
  model.ErrorDto
&lt;/h2&gt;

&lt;p&gt;This Dto can be used to send back erroneous response to the requestor.&lt;/p&gt;

&lt;h1&gt;
  
  
  model.PetDto
&lt;/h1&gt;

&lt;p&gt;The Dto that resembles our pets and is used to respond to, for instance, GET requests that want to retrieve data. Our generated code contains two examples of such routes: &lt;code&gt;listPets&lt;/code&gt; and &lt;code&gt;showPetById&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Implement the Delegate
&lt;/h1&gt;

&lt;p&gt;Now it's finally time to start coding. We create a new class in &lt;code&gt;nl.bjornvanderlaan.petstoreapi.api&lt;/code&gt; package (in &lt;code&gt;src&lt;/code&gt;, not &lt;code&gt;target&lt;/code&gt;!!) and we override the &lt;code&gt;listPets&lt;/code&gt; method to let it return an hardcoded response with my three favorite cat names and status 200 (Ok).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;nl.bjornvanderlaan.petstoreapi.api&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.http.HttpStatus&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.http.ResponseEntity&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.stereotype.Component&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;nl.bjornvanderlaan.petstoreapi.model.PetDto&lt;/span&gt;

&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PetsApiDelegateImpl&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PetsApiDelegate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;listPets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;?):&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PetDto&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;PetDto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Charlie"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;PetDto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Obi"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="nc"&gt;PetDto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Loki"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nc"&gt;HttpStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;OK&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h1&gt;
  
  
  Verify the API
&lt;/h1&gt;

&lt;p&gt;Great, so we have some generated classes in our &lt;code&gt;target&lt;/code&gt; directory based on our OpenAPI specification and we also added some code of our own in &lt;code&gt;src&lt;/code&gt;. Let's run this with IntelliJ and see what happens.&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%2F0q7lfoybp3vns59n907q.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%2F0q7lfoybp3vns59n907q.png" alt="Spring Boot application is running on port 8080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We learn that the Spring Boot application is now running on port 8080. But does it actually work?&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; http://localhost:8080/pets
HTTP/1.1 200 
Content-Type: application/json
Transfer-Encoding: chunked
Date: &amp;lt;redacted my author&amp;gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:8080/pets
&lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:1,&lt;span class="s2"&gt;"name"&lt;/span&gt;:&lt;span class="s2"&gt;"Charlie"&lt;/span&gt;,&lt;span class="s2"&gt;"tag"&lt;/span&gt;:null&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:2,&lt;span class="s2"&gt;"name"&lt;/span&gt;:&lt;span class="s2"&gt;"Obi"&lt;/span&gt;,&lt;span class="s2"&gt;"tag"&lt;/span&gt;:null&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:3,&lt;span class="s2"&gt;"name"&lt;/span&gt;:&lt;span class="s2"&gt;"Loki"&lt;/span&gt;,&lt;span class="s2"&gt;"tag"&lt;/span&gt;:null&lt;span class="o"&gt;}]&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h1&gt;
  
  
  Compile your code
&lt;/h1&gt;

&lt;p&gt;We might suspect that this tutorial is finished as we have verified the API's response. However, when we try to compile our code using &lt;code&gt;mvn clean compile&lt;/code&gt; we are met with some red text: compilation fails because the generated classes cannot be resolved. &lt;/p&gt;

&lt;p&gt;Why does this happen? The problem is that the &lt;code&gt;kotlin-maven-plugin&lt;/code&gt; does not see &lt;code&gt;target/generated-sources/openapi&lt;/code&gt; as a source directory and so these files are not compiled after cleaning. We resolve this by explicitly adding the path to the plugin's configuration:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.jetbrains.kotlin&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;kotlin-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- omitted by author --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;compile&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;compile&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;sourceDirs&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;sourceDir&amp;gt;&lt;/span&gt;${project.build.directory}/generated-sources/kotlin/src/main/kotlin&lt;span class="nt"&gt;&amp;lt;/sourceDir&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/sourceDirs&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- omitted by author --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Adding the above execution resolves our issue and Maven now tells us the build is successful.&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%2Fyffbeoyxab0ustz5sjk6.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%2Fyffbeoyxab0ustz5sjk6.png" alt="Maven compile successful"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! Now we are done, right? Not exactly. When we go a step further and run our Spring Boot application via &lt;code&gt;mvn spring-boot:run&lt;/code&gt; then we are met with another error:&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%2F6ntczw2wc8caft5m3eb8.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%2F6ntczw2wc8caft5m3eb8.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So now what? The issues is caused by our design choice to generate not only the interfaces but also the Controller classes as the generator then also gives us an &lt;code&gt;Application.kt&lt;/code&gt; class. As we already also have a main class in our &lt;code&gt;src&lt;/code&gt; project, Spring now cannot determine which to pick. There are essentially two ways to fix this. The first option is to delete our own &lt;code&gt;PetStoreApplication.kt&lt;/code&gt; and use the generated class. This could be a reasonable choice if you are use that you do not want to do anything custom in the main class. Alternatively, we can configure &lt;code&gt;spring-boot-maven-plugin&lt;/code&gt; to point out the correct main class:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;mainClass&amp;gt;&lt;/span&gt;nl.bjornvanderlaan.petstoreapi.PetstoreApiApplicationKt&lt;span class="nt"&gt;&amp;lt;/mainClass&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Regardless of which option you choose, we can run &lt;code&gt;mvn spring-boot:run&lt;/code&gt; again and are then at last greeted with our beloved Spring banner and the message that our application can now be found at port 8080!&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;OpenApi allows us to create a specification for our API that we can share among teams and stakeholders to communicate our contracts. The OpenAPI Generator and its Maven plugin help us to generate code from our specification. We ourselves can then focus on the business logic. In this tutorial, we saw how we can generate a server stud from our specification for Spring Boot-based Kotlin projects.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>springboot</category>
      <category>openapi</category>
      <category>maven</category>
    </item>
  </channel>
</rss>
