<?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: Igor Wojda</title>
    <description>The latest articles on DEV Community by Igor Wojda (@igorwojda).</description>
    <link>https://dev.to/igorwojda</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%2F378606%2F57dfaefc-8e90-4e9e-ad35-ea7db9787785.png</url>
      <title>DEV Community: Igor Wojda</title>
      <link>https://dev.to/igorwojda</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/igorwojda"/>
    <language>en</language>
    <item>
      <title>Refactoring Multi-Module Kotlin Project With Konsist</title>
      <dc:creator>Igor Wojda</dc:creator>
      <pubDate>Mon, 04 Sep 2023 15:22:56 +0000</pubDate>
      <link>https://dev.to/igorwojda/refactoring-multi-module-kotlin-project-with-konsist-57ge</link>
      <guid>https://dev.to/igorwojda/refactoring-multi-module-kotlin-project-with-konsist-57ge</guid>
      <description>&lt;h2&gt;
  
  
  Refactoring Multi-Module Kotlin Project With Konsist
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pYaUMTU0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/6816/1%2ABaZpGNdB3_NCzLMtdnS3MA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pYaUMTU0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/6816/1%2ABaZpGNdB3_NCzLMtdnS3MA.png" alt="" width="800" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Refactoring is not just about altering code; it’s about enhancing its structure, improving readability, optimizing performance, and keeping things consistent. In this article, we’ll focus on the consistency aspect and refactor a simple imaginary project to unify its codebase.&lt;/p&gt;

&lt;p&gt;We will also implement a set of guards to keep things &lt;strong&gt;K&lt;/strong&gt;onsistent in the future. To achieve this we will utilise the &lt;a href="https://github.com/LemonAppDev/konsist"&gt;Konsist&lt;/a&gt;, the Kotlin architectural linter.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Read &lt;a href="https://blog.kotlin-academy.com/introducing-konsist-a-cutting-edge-kotlin-linter-d3ab916a5461"&gt;Introduction to Konsist&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Base Project
&lt;/h2&gt;

&lt;p&gt;Typical projects are complex, they contain many types of classes/interfaces heaving various responsibilities (views, controllers, models, use cases, repositories, etc.). These classes/interfaces are usually spread across different modules and placed in various packages. Refactoring such a project would be too much for a single article, so we will refactor with a starter project heaving 3 modules and 4 use cases — the imaginary MyDiet application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you prefer learning by doing you can follow the article steps. Just check out the &lt;a href="https://github.com/LemonAppDev/konsist-article-refactoring-multi-module-project"&gt;repository&lt;/a&gt;, Open the starter project in the &lt;a href="https://www.jetbrains.com/idea/"&gt;InteliJ IDEA&lt;/a&gt; (&lt;code&gt;idea-mydiet-starter&lt;/code&gt;) or &lt;a href="https://developer.android.com/studio"&gt;Android Studio&lt;/a&gt; (&lt;code&gt;android-studio-mydiet-starter&lt;/code&gt;). To keep things simple this project contains a set of classes to be verified and refactored, not the full-fledge app.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The MyDiet application has feature 3 modules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;featureCaloryCalculator&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;featureGroceryListGenerator&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;featureMealPlanner&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZWMlWzFx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2A9LOlQ2qq3Ca6PFDv8s8adw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZWMlWzFx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2A9LOlQ2qq3Ca6PFDv8s8adw.png" alt="" width="800" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each feature module has one or more use cases. Let’s look at the familiar &lt;em&gt;project view&lt;/em&gt; from &lt;a href="https://www.jetbrains.com/idea/"&gt;IntelliJ IDEA&lt;/a&gt; to get the full project structure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AibclLZ2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2Avl7ZFq5V_3cOFhkc_4zk9A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AibclLZ2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2Avl7ZFq5V_3cOFhkc_4zk9A.png" alt="" width="800" height="1373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s look at the content of each use case classes across all feature modules:&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="c1"&gt;// featureCaloryCalculator module&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdjustCaloricGoalUseCase&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;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// business logic&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;calculateCalories&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// business logic&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;CalculateDailyIntakeUseCase&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;execute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// business logic&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// featureGroceryListGenerator module&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CategorizeGroceryItemsUseCase&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;categorizeGroceryItemsUseCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// business logic&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// featureMealPlanner module&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlanWeeklyMealsUseCase&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;invoke&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// business logic&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;Use case holds the business logic (for simplicity represented here as a comment in the code). At first glance, these use cases look similar but after closer examination, you will notice that the use case class declarations are inconsistent when it comes to method names, number of public methods, and packages. Most likely because these use cases were written by different developers prioritizing developer personal opinions rather than project-specific standards.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Exact rules will vary from project to project, but Konsist API can still be used to define checks tailored for a specific project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s write a few Konsist tests to unify the codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guard 1: Unify The UseCase Method Names
&lt;/h2&gt;

&lt;p&gt;Let’s imagine that this is a large-scale project containing many classes in each module and because each module is large we want to refactor each module in isolation. Per module, refactoring will limit the scope of changes and will help with keeping Pull Request smaller. We will focus only on unifying use cases.&lt;/p&gt;

&lt;p&gt;The first step of using Konsist is creation of the scope (containing a list of Kotlin files) present in a given module:&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="nc"&gt;Konsist&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"featureCaloryCalculator"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Kotlin files in featureCaloryCalculator module&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to select all classes representing use cases. In this project use case is a class with UseCase name suffix ( .withNameEndingWith(“UseCase”)).&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="nc"&gt;Konsist&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"featureCaloryCalculator"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withNameEndingWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UseCase"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;In other projects use case could be represented by the class extending BaseUseCase class (&lt;code&gt;.withAllParentsOf*(BaseUseCase::class)&lt;/code&gt;) or every class annotated with the @UseCase annotation (&lt;code&gt;.withAllAnnotationsOf(BaseUseCase::class)&lt;/code&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now define the assert containing desired checks (the last line of the assert block always has to return a boolean). We will make sure that every use case has a public method with a unified name. We will choose the invoke as a desired method name:&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="nc"&gt;Konsist&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"featureCaloryCalculator"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withNameEndingWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UseCase"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&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;containsFunction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="n"&gt;function&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="s"&gt;"invoke"&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hasPublicOrDefaultModifier&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;Notice that our guard treats the absence of visibility modifier as &lt;code&gt;public&lt;/code&gt;, because it is a default Kotlin visibility.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you would like always heave an explicit public visibility modifier you could use &lt;code&gt;hasPublicModifier&lt;/code&gt; property instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To make the above check work we need to wrap it in &lt;a href="https://junit.org/"&gt;JUnit&lt;/a&gt; test:&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;@Test&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;`featureCaloryCalculator&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;UseCase&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;named&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;invoke&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;span class="nc"&gt;Konsist&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"featureCaloryCalculator"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withNameEndingWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UseCase"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&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;containsFunction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="n"&gt;function&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="s"&gt;"invoke"&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hasPublicOrDefaultModifier&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;blockquote&gt;
&lt;p&gt;If you are following with the project add this test to app/src/test/kotlin/UseCaseKonsistTest.kt file. To run Konsist test &lt;a href="https://www.jetbrains.com/help/idea/performing-tests.html"&gt;click on the green arrow&lt;/a&gt; (left to the test method name).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After running Konsist test it will complain about lack of the method named invoke in the &lt;code&gt;AdjustCaloricGoalUseCase&lt;/code&gt; and &lt;code&gt;CalculateDailyIntakeUseCase&lt;/code&gt; classes (&lt;code&gt;featureCaloryCalculator&lt;/code&gt; module). Let’s update method names in these classes to make the test pass:&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="c1"&gt;// featureCaloryCalculator module&lt;/span&gt;
    &lt;span class="c1"&gt;// BEFORE&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdjustCaloricGoalUseCase&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;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// business logic&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;calculateCalories&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// business logic&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;CalculateDailyIntakeUseCase&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;execute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// business logic&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// AFTER&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdjustCaloricGoalUseCase&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;invoke&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// CHANGE: Name updated&lt;/span&gt;
            &lt;span class="c1"&gt;// business logic&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;calculateCalories&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// business logic&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;CalculateDailyIntakeUseCase&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;invoke&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// CHANGE: Name updated&lt;/span&gt;
            &lt;span class="c1"&gt;// business logic&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;The next module to refactor is the &lt;code&gt;featureGroceryListGenerator&lt;/code&gt; module. Again we will assume that this is a very large module containing many classes and interferes. We can simply copy the test and update the module names:&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;@Test&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;`featureCaloryCalculator&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;UseCase&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;named&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;invoke&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;span class="nc"&gt;Konsist&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"featureCaloryCalculator"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withNameEndingWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UseCase"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&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;containsFunction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="n"&gt;function&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="s"&gt;"invoke"&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hasPublicOrDefaultModifier&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="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;`featureGroceryListGenerator&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;UseCase&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;named&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;invoke&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;span class="nc"&gt;Konsist&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"featureGroceryListGenerator"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withNameEndingWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UseCase"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&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;containsFunction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="n"&gt;function&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="s"&gt;"invoke"&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hasPublicOrDefaultModifier&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;The above approach works, however it leads to unnecessary code duplication. We can do better by creating two scopes for each module and add them:&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;@Test&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;`classes&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;UseCase&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;named&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;invoke&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;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;featureCaloryCalculatorScope&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Konsist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"featureCaloryCalculator"&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;featureGroceryListGeneratorScope&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Konsist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"featureGroceryListGenerator"&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;refactoredModules&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;featureCaloryCalculatorScope&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;featureGroceryListGeneratorScope&lt;/span&gt;

        &lt;span class="n"&gt;refactoredModules&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withNameEndingWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UseCase"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&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;containsFunction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="n"&gt;function&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="s"&gt;"invoke"&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hasPublicOrDefaultModifier&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;blockquote&gt;
&lt;p&gt;Addition of scopes is possible because KoScope overrides Kotlin &lt;code&gt;plus&lt;/code&gt; and &lt;code&gt;plusAssign&lt;/code&gt; operators. See &lt;a href="https://docs.konsist.lemonappdev.com/writing-tests/koscope"&gt;Create The Scope&lt;/a&gt; for more information.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This time the Konsist test will fail because the &lt;code&gt;CategorizeGroceryItemsUseCase&lt;/code&gt; class present in the &lt;code&gt;featureGroceryListGenerator&lt;/code&gt; module has an incorrect name. Let’s fix that:&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="c1"&gt;// featureGroceryListGenerator module&lt;/span&gt;
    &lt;span class="c1"&gt;// BEFORE&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CategorizeGroceryItemsUseCase&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;categorizeGroceryItemsUseCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// business logic&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// AFTER&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CategorizeGroceryItemsUseCase&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;invoke&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// CHANGE: Name updated&lt;/span&gt;
            &lt;span class="c1"&gt;// business logic&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;The test is passing. Now we have the last module to refactor. We can add another scope representing Kotlin files in the &lt;code&gt;featureMealPlanner&lt;/code&gt; module:&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;@Test&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;`classes&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;UseCase&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;named&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;invoke&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;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;featureCaloryCalculatorScope&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Konsist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"featureCaloryCalculator"&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;featureGroceryListGeneratorScope&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Konsist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"featureGroceryListGenerator"&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;featureMealPlannerScope&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Konsist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"featureMealPlanner"&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;refactoredModules&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; 
                 &lt;span class="n"&gt;featureCaloryCalculatorScope&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; 
                 &lt;span class="n"&gt;featureGroceryListGeneratorScope&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                 &lt;span class="n"&gt;featureMealPlannerScope&lt;/span&gt;

        &lt;span class="n"&gt;refactoredModules&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withNameEndingWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UseCase"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&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;containsFunction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="n"&gt;function&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="s"&gt;"invoke"&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hasPublicOrDefaultModifier&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;Notice that the &lt;code&gt;featureMealPlanner&lt;/code&gt; module is the last module for this particular refactoring, so we can simplify the above code. Rather than creating 3 separate scopes (for each module) and adding them ,we can verify all classes present in the production source set (main) by using &lt;code&gt;Konsist.scopeFromProject()&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;@Test&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;`classes&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;UseCase&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;named&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;invoke&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;span class="nc"&gt;Konsist&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromProject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withNameEndingWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UseCase"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&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;containsFunction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="n"&gt;function&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="s"&gt;"invoke"&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hasPublicOrDefaultModifier&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 time the test will succeed, because the &lt;code&gt;PlanWeeklyMealsUseCase&lt;/code&gt; class present in the &lt;code&gt;featureMealPlanner&lt;/code&gt; module already has a method named &lt;code&gt;invoke&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlanWeeklyMealsUseCase&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;invoke&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// INFO: Already had correct method name&lt;/span&gt;
            &lt;span class="c1"&gt;// business logic&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;Let’s improve our rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guard 2: Use Case Has Only One Public Method
&lt;/h2&gt;

&lt;p&gt;To verify if every use case present in the project has a single public method we can check the number of public (or default) declarations in the class by using &lt;code&gt;it.numPublicOrDefaultDeclarations() == 1&lt;/code&gt;. Instead of writing a new test we can just improve the existing one:&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;@Test&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;`classes&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;UseCase&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;single&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="n"&gt;named&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;invoke&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;span class="nc"&gt;Konsist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromProject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withNameEndingWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UseCase"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&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;hasSingleInvokeMethod&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;containsFunction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                  &lt;span class="n"&gt;function&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="s"&gt;"invoke"&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hasPublicOrDefaultModifier&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;hasSinglePublicDeclaration&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;numPublicOrDefaultDeclarations&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="n"&gt;hasSingleInvokeMethod&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;hasSinglePublicDeclaration&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;After running this konsist test we will realise that the &lt;code&gt;AdjustCaloricGoalUseCase&lt;/code&gt; class has two &lt;code&gt;public&lt;/code&gt; methods. To fix we will change visibility of the &lt;code&gt;calculateCalorie&lt;/code&gt;s method to &lt;code&gt;private&lt;/code&gt; (we assume it was accidentally exposed):&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="c1"&gt;// featureCaloryCalculator module&lt;/span&gt;
    &lt;span class="c1"&gt;// BEFORE&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdjustCaloricGoalUseCase&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;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// business logic&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;calculateCalories&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// business logic&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// AFTER&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdjustCaloricGoalUseCase&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;invoke&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// business logic&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;calculateCalories&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// CHANGE: Visibility updated&lt;/span&gt;
            &lt;span class="c1"&gt;// business logic&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;
  
  
  Guard 3: Every Use Case Resides in “domain.usecase” package
&lt;/h2&gt;

&lt;p&gt;You may not have noticed yet, but use case package structure is a bit off. Two use cases &lt;code&gt;AdjustCaloricGoalUseCase&lt;/code&gt; and &lt;code&gt;CalculateDailyIntakeUseCase&lt;/code&gt; classes resides in the &lt;code&gt;com.mydiet&lt;/code&gt; package, &lt;code&gt;CategorizeGroceryItemsUseCase&lt;/code&gt; class resides in the &lt;code&gt;com.mydiet.usecase&lt;/code&gt; package(no &lt;code&gt;s&lt;/code&gt; at the end) and &lt;code&gt;PlanWeeklyMealsUseCase&lt;/code&gt; class resides in the &lt;code&gt;com.mydiet.usecases&lt;/code&gt; package (s at the end):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tFBt8fqt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2A3vjed1WSO3c7izV8ekFQrA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tFBt8fqt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2A3vjed1WSO3c7izV8ekFQrA.png" alt="" width="800" height="1373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will start by verifying if the desired package for each use case is &lt;code&gt;domain.usecase package&lt;/code&gt; (prefixed and followed by an number of packages). Updating package names is quite straight forward task so this time we will define guard for all modules and fix all violations in one go. Let’s write a new Konsist test to guard this standard:&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;@Test&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;`classes&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;UseCase&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;reside&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;domain&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="n"&gt;usecase&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="nf"&gt;packages`&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Konsist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromProduction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withNameEndingWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UseCase"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&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;resideInPackage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"..domain.usecase.."&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;blockquote&gt;
&lt;p&gt;Two dots &lt;code&gt;..&lt;/code&gt; means zero or more packages.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The test highlighted above will now fail for all use cases because none of them reside in the correct package (none of them reside in the &lt;code&gt;domain&lt;/code&gt; package). To fix this we have to simply update the packages (class content is omitted for clarity):&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="c1"&gt;// BEFORE&lt;/span&gt;
    &lt;span class="c1"&gt;// featureCaloryCalculator module&lt;/span&gt;
    &lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.mydiet&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdjustCaloricGoalUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* .. */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.mydiet&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CalculateDailyIntakeUseCase&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* .. */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// featureGroceryListGenerator module&lt;/span&gt;
    &lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.mydiet.usecase&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CategorizeGroceryItemsUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* .. */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// featureMealPlanner module&lt;/span&gt;
    &lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.mydiet.usecases&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlanWeeklyMealsUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* .. */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// AFTER&lt;/span&gt;
    &lt;span class="c1"&gt;// featureCaloryCalculator module&lt;/span&gt;
    &lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.mydiet.domain.usecase&lt;/span&gt; &lt;span class="c1"&gt;// CHANGE: Package updated&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdjustCaloricGoalUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* .. */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.mydiet.domain.usecase&lt;/span&gt; &lt;span class="c1"&gt;// CHANGE: Package updated&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CalculateDailyIntakeUseCase&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* .. */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// featureGroceryListGenerator module&lt;/span&gt;
    &lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.mydiet.domain.usecase&lt;/span&gt; &lt;span class="c1"&gt;// CHANGE: Package updated&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CategorizeGroceryItemsUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* .. */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// featureMealPlanner module&lt;/span&gt;
    &lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.mydiet.domain.usecase&lt;/span&gt; &lt;span class="c1"&gt;// CHANGE: Package updated&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlanWeeklyMealsUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* .. */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now Konsist tests will succeed. We can improve package naming even more. In a typical project every class present in a feature module would have a package prefixed with the feature name to avoid class redeclaration across different modules. We can retrieve module name (&lt;code&gt;moduleName&lt;/code&gt;), and remove &lt;code&gt;feature&lt;/code&gt; prefix to get the name of the package. Let’s improve existing test:&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;@Test&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;`classes&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="nc"&gt;UseCase&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="n"&gt;suffix&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;reside&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;usecase&lt;/span&gt; &lt;span class="nf"&gt;packages`&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Konsist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromProduction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withNameEndingWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UseCase"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="cm"&gt;/*
                module -&amp;gt; package name:
                featureMealPlanner -&amp;gt; mealplanner
                featureGroceryListGenerator -&amp;gt; grocerylistgenerator
                featureCaloryCalculator -&amp;gt; calorycalculator
                */&lt;/span&gt;
                &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;featurePackageName&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="n"&gt;containingFile&lt;/span&gt;
                  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;moduleName&lt;/span&gt;
                  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lowercase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removePrefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"feature"&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;resideInPackage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"..${featurePackageName}.domain.usecase.."&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;And the final fix to update these packages once again:&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="c1"&gt;// BEFORE&lt;/span&gt;
    &lt;span class="c1"&gt;// featureCaloryCalculator module&lt;/span&gt;
    &lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.mydiet.domain.usecase&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdjustCaloricGoalUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* .. */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.mydiet.domain.usecase&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CalculateDailyIntakeUseCase&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* .. */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// featureGroceryListGenerator module&lt;/span&gt;
    &lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.mydiet.domain.usecase&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CategorizeGroceryItemsUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* .. */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// featureMealPlanner module&lt;/span&gt;
    &lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.mydiet.domain.usecase&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlanWeeklyMealsUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* .. */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// AFTER&lt;/span&gt;
    &lt;span class="c1"&gt;// featureCaloryCalculator module&lt;/span&gt;
    &lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.mydiet.calorycalculator.domain.usecase&lt;/span&gt; &lt;span class="c1"&gt;// CHANGE: Package updated&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdjustCaloricGoalUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* .. */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.mydiet.calorycalculator.domain.usecase&lt;/span&gt; &lt;span class="c1"&gt;// CHANGE: Package updated&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CalculateDailyIntakeUseCase&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* .. */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// featureGroceryListGenerator module&lt;/span&gt;
    &lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.mydiet.grocerylistgenerator.domain.usecase&lt;/span&gt; &lt;span class="c1"&gt;// CHANGE: Package updated&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CategorizeGroceryItemsUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* .. */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// featureMealPlanner module&lt;/span&gt;
    &lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.mydiet.mealplanner.domain.usecase&lt;/span&gt; &lt;span class="c1"&gt;// CHANGE: Package updated&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlanWeeklyMealsUseCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* .. */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of the uses cases are guarded by set of Konsist tests meaning that project coding standards are enforced.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;See &lt;a href="https://github.com/LemonAppDev/konsist-article-refactoring-multi-module-project"&gt;mydiet-complete&lt;/a&gt; project containing all tests and updated code in the &lt;a href="https://github.com/LemonAppDev/konsist-article-refactoring-multi-module-project"&gt;GitHub repository&lt;/a&gt; .&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Konsist tests are verifying all classes present in the project at scope creation time meaning that every use case added in the future will be verified by the above guards.&lt;/p&gt;

&lt;p&gt;Konsist can help you with guarding even more aspects of the use case. Perhaps you are migrating from &lt;code&gt;RxJava&lt;/code&gt; to Kotlin &lt;code&gt;Flow&lt;/code&gt; and you would like to verify the type returned by invoke method or verify that every &lt;code&gt;invoke&lt;/code&gt; method has an &lt;code&gt;operator&lt;/code&gt; modifier. It is also possible to make sure that every use case constructor parameter has a name derived from the type or make sure that there parameters are ordered in desired order (e.g. alphabetically).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Konists tests are intended to run as part of Pull Request code verification, similar to classic unit tests.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This was a very simple yet comprehensive example demonstrating how Konsist can help with code base unification and enforcement of project-specific rules. Upon inspection, we found inconsistencies in method names and declarations, likely due to multiple developers inputs. To address this, we employ Konsist, a Kotlin architectural linter.&lt;/p&gt;

&lt;p&gt;In the real world, projects will be more complex, heaving more classes, interfaces, more modules, and will require more Konsist tests. These guards will slightly differ for every project, but fortunately, they can be captured by Konsist flexible API. With Konsist tests in place, we ensure future additions maintain code consistency, making the codebase more navigable and understandable. The code will be &lt;strong&gt;Konsistant&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👉 Follow me on &lt;a href="https://twitter.com/igorwojda"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://blog.kotlin-academy.com/introducing-konsist-a-cutting-edge-kotlin-linter-d3ab916a5461"&gt;Introduction to Konsist&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/LemonAppDev/konsist"&gt;Konsist project repository&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.konsist.lemonappdev.com/"&gt;Konsist documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://kotlinlang.slack.com/archives/C05QG9FD6KS"&gt;Konsist Slack channel (at kotlinlang)&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/igorwojda/android-showcase/"&gt;Android-Showcase&lt;/a&gt; (Android project using Konsist)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>konsist</category>
      <category>android</category>
      <category>spring</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Introducing Konsist: A Cutting-Edge Kotlin Linter</title>
      <dc:creator>Igor Wojda</dc:creator>
      <pubDate>Wed, 23 Aug 2023 09:30:00 +0000</pubDate>
      <link>https://dev.to/igorwojda/introducing-konsist-a-cutting-edge-kotlin-linter-1b5f</link>
      <guid>https://dev.to/igorwojda/introducing-konsist-a-cutting-edge-kotlin-linter-1b5f</guid>
      <description>

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

&lt;p&gt;Linters are vital tools in software development, helping to enforce code standards and best practices across the code base. By analysing code for errors and inconsistencies, linters improve collaboration, code quality, and adherence to coding standards, especially in complex projects.&lt;/p&gt;

&lt;p&gt;Several linters are already available for &lt;a href="https://kotlinlang.org/"&gt;Kotlin&lt;/a&gt; programming language. Rules for some linters may overlap e.g. both &lt;a href="https://pinterest.github.io/ktlint/0.50.0/"&gt;ktlint&lt;/a&gt; and &lt;a href="https://detekt.dev/"&gt;detekt&lt;/a&gt; have a "Formatting Rule Set", however, each linter was created with a different focus in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ktlint - enforces Kotlin coding conventions. Its &lt;a href="https://pinterest.github.io/ktlint/0.49.1/rules/standard/"&gt;rules&lt;/a&gt; are mostly focused on code naming and code formatting (spacing, braces, empty blocks, line length, etc) .&lt;/li&gt;
&lt;li&gt;Detekt -aimed at finding various code smells. Its &lt;a href="https://detekt.dev/docs/rules/comments/"&gt;rules&lt;/a&gt; are mostly focused on code smells, complexity, performance, and potential bugs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you probably wonder why do we need another linter? Why do we need &lt;a href="https://github.com/LemonAppDev/konsist"&gt;Konsist&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;Well, let me tell you a short story…&lt;/p&gt;

&lt;h1&gt;
  
  
  Meet Jill, The Kotlin Developer
&lt;/h1&gt;

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

&lt;p&gt;&lt;em&gt;Jill had always been passionate about coding, and the opportunity to work as a Kotlin developer was a dream come true. She had just joined a thriving tech company, eager to contribute her skills and learn from her talented colleagues.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A few days into her role, Jill was assigned her first significant task: adding a new use case to the project. Excitement bubbled within her. This was a chance to prove herself, to make a meaningful impact. The task seemed straightforward enough, but she wanted to make sure she aligned her work with the established patterns of the codebase.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;So, Jill began exploring the existing use cases, opening file after file, and digging into the structures and logic. She expected to find a consistent pattern, something that would guide her in crafting the new use case. But what she found was a bit overwhelming - every use case was different…&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We all have been here - we join a new project and we need some time to figure out what is going on. Projects are often complex, and often this complexity is a direct result of an inconsistent, tangled and messy code base.&lt;/p&gt;

&lt;p&gt;In the lifecycle of a project, as it scales and matures, and as developers come and go, integrating new features, addressing bugs, and applying patches usually compromise the code consistency.&lt;/p&gt;

&lt;p&gt;The team can agree on codebase standards, but enforcing them is another story. Developers are not perfect, so it is quite easy to miss a thing or two while reviewing a PR.&lt;/p&gt;

&lt;p&gt;Some standards are hard or impossible to guard using existing linters and thus many guards are usually not automated. Without automated guards, the codebase quality tends to degrade over time.&lt;/p&gt;

&lt;p&gt;Both ktlint and detekt work on the file level - they are verifying files one by one, checking each file in isolation.&lt;/p&gt;

&lt;h1&gt;
  
  
  Meet Konsist
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/LemonAppDev/konsist"&gt;Konsist&lt;/a&gt; is a new cutting edge linter. Konsist focuses on capturing project-specific coding standards across similar types of code declarations. Let's explore a few high-level usages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every child class extending ViewModel must have "ViewModel" suffix&lt;/li&gt;
&lt;li&gt;Classes with the &lt;code&gt;@Repository&lt;/code&gt; annotation should reside in &lt;code&gt;repository&lt;/code&gt; package&lt;/li&gt;
&lt;li&gt;The presentation layer (defined by the &lt;code&gt;presentation&lt;/code&gt; package) can only access classes from the domain layer (defined by the &lt;code&gt;domain&lt;/code&gt; package)&lt;/li&gt;
&lt;li&gt;Every use case constructor has alphabetically ordered parameters&lt;/li&gt;
&lt;li&gt;Every repository constructor parameter has a name derived from the class name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Konsist provides two types of guards to protect the codebase - declaration guards and architectural guards.&lt;/p&gt;

&lt;h1&gt;
  
  
  Konsist Declaration Guards
&lt;/h1&gt;

&lt;p&gt;Let's look at some practical applications. Let's review some code examples that demonstrate Konsist API and how it can be used to improve the quality of Kotlin code.&lt;/p&gt;

&lt;p&gt;Consider this code snippet containing three declarations - &lt;code&gt;com.myapp.logger&lt;/code&gt; package, &lt;code&gt;logLevel&lt;/code&gt; property and &lt;code&gt;FileLogger&lt;/code&gt; class:&lt;br&gt;
package com.myapp.logger&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;internal&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;logLevel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"debug"&lt;/span&gt;

&lt;span class="nd"&gt;@LoggerEntity&lt;/span&gt;
&lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FileLogger&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;level&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="nc"&gt;BaseLogger&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&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="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;There are a few things we can check in the above snippet. Let's start with getting the declaration names. The entry point of the Konsist linter is the Konsist class. For start lets query available declarations (of all types) and simply map their names:&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="nc"&gt;Konsist&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromProject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// create scope from all project files&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;declarations&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// get declarations&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&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="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// "com.myapp.logger", "loglevel", "FileLogger"&lt;/span&gt;
    &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Konsist API can be mixed with known Kotlin collection processing API (the above map function) providing additional processing and filtering capabilities.&lt;/p&gt;

&lt;p&gt;Mapping names will help us to understand how to retrieve various declarations and debug existing guards, however to define actual guards we must also verify whatever or not these declarations meet a certain criteria using assert function.&lt;/p&gt;

&lt;p&gt;On a high-level Konsist declaration check contains 3 steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deciding what you want to check - e.g. classes…&lt;/li&gt;
&lt;li&gt;Querying the desired declarations - e.g. …extending BaseUseCase class…&lt;/li&gt;
&lt;li&gt;Defining an assertion to perform a check - e.g. …must have an internal modifier.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's start with checking if all classes extending a BaseLogger class (like above FileLogger) have a name ending withLogger:&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="nc"&gt;Konsist&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromProject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withParentClassOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BaseLogger&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&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="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Logger"&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;We can also verify if all classes present in logger package are annotated withLoggerEntity annotation:&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="nc"&gt;Konsist&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromProject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resideInPackage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"..logger.."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&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;hasAnnotations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LoggerEntity"&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;To make Konsist guard work we need to wrap above code in unit test - we will call such guard a &lt;code&gt;Konstst test&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;@Test&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;`classes&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;are&lt;/span&gt; &lt;span class="n"&gt;annotated&lt;/span&gt; &lt;span class="nf"&gt;withLoggerEntity`&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;Konsist&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromProject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resideInPackage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"..logger.."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert&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;hasAnnotations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LoggerEntity"&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;blockquote&gt;
&lt;p&gt;The &lt;code&gt;@Test&lt;/code&gt; annotation is part of &lt;a href="https://junit.org/junit5/"&gt;JUnit&lt;/a&gt; framework.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The test name provides description for the given guard, while self explanatory Konsist API allows to easily determine scope of the particular check.&lt;/p&gt;

&lt;h1&gt;
  
  
  Konsist Architecture Guards
&lt;/h1&gt;

&lt;p&gt;The above examples demonstrate usage of the declaration guards. Konsist also provides a dedicated API for dependency verification that can assist you in guarding application layers. These guards ensure that a given class (which is part of a conceptual layer) can only use classes defined in a different package (another conceptual layer). Consider the following test, which guards the Clean Architecture layers of application residing in &lt;code&gt;com.myapp&lt;/code&gt; package:&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;`clean&lt;/span&gt; &lt;span class="n"&gt;architecture&lt;/span&gt; &lt;span class="n"&gt;layers&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt; &lt;span class="n"&gt;correct&lt;/span&gt; &lt;span class="nf"&gt;dependencies`&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;Konsist&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scopeFromProduction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertArchitecture&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="c1"&gt;// Define layers&lt;/span&gt;
              &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;domain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Layer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Domain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"com.myapp.domain.."&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;presentation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Layer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Presentation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"com.myapp.presentation.."&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;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Layer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"com.myapp.data.."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

              &lt;span class="c1"&gt;// Define architecture assertions&lt;/span&gt;
              &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dependsOnNothing&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
              &lt;span class="n"&gt;presentation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dependsOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dependsOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domain&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 guard is helpful when architectural layers are stored in packages rather than modules.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Take a read of extensive &lt;a href="https://docs.konsist.lemonappdev.com/getting-started/readme"&gt;Konsist documentation&lt;/a&gt; and review &lt;a href="https://docs.konsist.lemonappdev.com/inspiration/snippets"&gt;snippets&lt;/a&gt; section for more examples.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Konsist API
&lt;/h1&gt;

&lt;p&gt;Konsist API makes it easy for developers to define custom, concise, and expressive guards to check the correctness of the project code. Konsist can assert every declaration present in code - naming conventions, annotations, order and types of parameters, package structure, inheritance, modifiers, dependencies, architectural layers, and more.&lt;/p&gt;

&lt;p&gt;Most linters work fine out of the box providing build-in rules, however, they usually don't scale well - writing a custom rule is often a complex task. It usually requires learning (not so obvious) internal linter extension API and simple conditions may result in quite verbose code. This is where Konsist shines - the Konsist API reflects declarations visible in Kotlin code, so API is much more intuitive for the developers. Konsist API mimics the structure of Kotlin code allowing it to quickly query Kotlin declarations such as files, classes, interfaces, functions, properties etc.&lt;/p&gt;

&lt;h1&gt;
  
  
  Konsist Project Status
&lt;/h1&gt;

&lt;p&gt;Konsist library has been extensively tested on a variety of Spring and Android projects with both Gradle and Maven build systems. In addition, Konsist has a robust test suite (over 1300 tests and 20 unique CI checks) to prevent regression bugs.&lt;br&gt;
Konsist is safe to use because it is not part of the production code base, but rather only used to verify the code base (similar to JUnit and other testing frameworks).&lt;/p&gt;

&lt;p&gt;Konsist API has undergone multiple iterations to make it simpler to use and more versatile. At this stage, Konsist API should allow you to express most of the codebase guards you have in mind.&lt;/p&gt;

&lt;p&gt;The Konsist project has reached the 2nd Milestone (Project Status page) and is actively seeking community feedback to help it mature:&lt;/p&gt;

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

&lt;h1&gt;
  
  
  Community Engagement
&lt;/h1&gt;

&lt;p&gt;The Konsist project is now at a critical stage where community input is essential to polish it. Currently, the project needs external input and field testing to ensure that it is ready for widespread use. Please play with Konsist, try it out, and let us know what works, what features you would like to see, and what can be improved. Your feedback is crucial at this stage as it will help Konsist to mature.&lt;/p&gt;

&lt;p&gt;If you find this project promising, you can show your support by starring it on GitHub, tweeting about it, or writing a blog post. This will help to raise awareness of the project, attract more contributors and mature this project faster.&lt;/p&gt;

&lt;p&gt;Thank you for your support! 🙏&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;Konsist is a new, flexible linter that allows writing custom, project-specific rules to guard project code consistency. It accomplishes other linters offering a more familiar API that mimics Kotlin code declarations. Konsist fits into continuous integration pipelines and development workflows. For more information please review the &lt;a href="https://docs.konsist.lemonappdev.com/getting-started/readme"&gt;Konsist documentation&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Follow me on &lt;a href="https://twitter.com/igorwojda"&gt;Twitter(X)&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Links:
&lt;a href="https://github.com/LemonAppDev/konsist"&gt;Konsist project repository&lt;/a&gt;
&lt;a href="https://docs.konsist.lemonappdev.com/getting-started/readme"&gt;Konsist documentation&lt;/a&gt;
&lt;a href="https://github.com/igorwojda/android-showcase/"&gt;Android-Showcase (Android project using Konsist)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kotlin</category>
      <category>android</category>
      <category>androiddev</category>
      <category>spring</category>
    </item>
    <item>
      <title>Introducing Konsist - cutting-edge Kotlin linter</title>
      <dc:creator>Igor Wojda</dc:creator>
      <pubDate>Wed, 23 Aug 2023 08:24:42 +0000</pubDate>
      <link>https://dev.to/igorwojda/introducing-konsist-cutting-edge-kotlin-linter-4839</link>
      <guid>https://dev.to/igorwojda/introducing-konsist-cutting-edge-kotlin-linter-4839</guid>
      <description>&lt;p&gt;The time has finally come to share the new Kotlin linter with the community. Enjoy!&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://blog.kotlin-academy.com/introducing-konsist-a-cutting-edge-kotlin-linter-d3ab916a5461"&gt;https://blog.kotlin-academy.com/introducing-konsist-a-cutting-edge-kotlin-linter-d3ab916a5461&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am eager to hear your feedback.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Build A 2FA Server With Kotlin and Ktor</title>
      <dc:creator>Igor Wojda</dc:creator>
      <pubDate>Wed, 20 Jan 2021 15:39:22 +0000</pubDate>
      <link>https://dev.to/igorwojda/build-a-2fa-server-with-kotlin-and-ktor-42bc</link>
      <guid>https://dev.to/igorwojda/build-a-2fa-server-with-kotlin-and-ktor-42bc</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--75Vbfo67--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k8x59l0iaeg8nghki1kc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--75Vbfo67--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k8x59l0iaeg8nghki1kc.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, you will write a server that provides an API for &lt;a href="https://en.wikipedia.org/wiki/Multi-factor_authentication"&gt;Two-factor Authentication (2FA)&lt;/a&gt;. This API will allow desktop clients, mobile clients, and web clients to utilize two-factor authentication.&lt;/p&gt;

&lt;p&gt;To build the application, you will use the &lt;a href="https://kotlinlang.org/"&gt;Kotlin&lt;/a&gt; language and &lt;a href="https://ktor.io/"&gt;Ktor&lt;/a&gt;, an asynchronous framework for creating microservices and web applications.&lt;/p&gt;

&lt;p&gt;The complete source code is available on &lt;a href="https://github.com/nexmo-community/ktor-2fa-server"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow along with this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.jetbrains.com/idea/download/"&gt;IntelliJ IDEA&lt;/a&gt; IDE installed (paid or free, community edition).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ktor.io/docs/intellij-idea.html"&gt;Ktor&lt;/a&gt; plugin for IntelliJ IDEA. This plugin allows you to create a Ktor project using a new project wizard. Open &lt;em&gt;IntelliJ IDEA&lt;/em&gt;, go to &lt;em&gt;Preferences&lt;/em&gt;, then &lt;em&gt;Plugins&lt;/em&gt;, and install a &lt;em&gt;Ktor&lt;/em&gt; plugin from the marketplace.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2&gt;
  
  
  Create A Ktor Project
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Open &lt;em&gt;IntelliJ IDEA&lt;/em&gt;, then go to &lt;em&gt;File &amp;gt; New &amp;gt; Project&lt;/em&gt;. &lt;/li&gt;
&lt;li&gt;In the &lt;em&gt;New Project&lt;/em&gt; window, select the &lt;em&gt;Ktor&lt;/em&gt; project on the left side and press the &lt;em&gt;Next&lt;/em&gt; button.&lt;/li&gt;
&lt;li&gt;On the next screen, leave the default values and press the &lt;em&gt;Next&lt;/em&gt; button.&lt;/li&gt;
&lt;li&gt;On the final screen, enter &lt;code&gt;ktor-2fa-server&lt;/code&gt; as the application name and press the &lt;em&gt;Finish&lt;/em&gt; button.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You have created a Ktor application project.&lt;/p&gt;

&lt;h2&gt;
  
  
  First endpoint
&lt;/h2&gt;

&lt;p&gt;Open the &lt;code&gt;src/Application.kt&lt;/code&gt; file and add a new &lt;code&gt;routing&lt;/code&gt; to verify that the application is working:&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="nc"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;routing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;get&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="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2FA app is working"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ContentType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Html&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;In this tutorial, all the Ktor application code will be stored in the &lt;code&gt;Application.kt&lt;/code&gt; file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Click on the green arrow next to the &lt;code&gt;main&lt;/code&gt; function to run the application (this will create a new run configuration in the IDE):&lt;/p&gt;

&lt;p&gt;&lt;a href="/content/blog/build-a-2fa-server-with-kotlin-and-ktor/run-app.png" class="article-body-image-wrapper"&gt;&lt;img src="/content/blog/build-a-2fa-server-with-kotlin-and-ktor/run-app.png" alt="Run app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to &lt;code&gt;http://localhost:8080/&lt;/code&gt; in your browser to test if the application is working correctly—"2FA app is working" should be displayed:&lt;/p&gt;

&lt;p&gt;&lt;a href="/content/blog/build-a-2fa-server-with-kotlin-and-ktor/app-is-working.png" class="article-body-image-wrapper"&gt;&lt;img src="/content/blog/build-a-2fa-server-with-kotlin-and-ktor/app-is-working.png" alt="App is working"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Developement Mode
&lt;/h2&gt;

&lt;p&gt;Enabling development mode allows the Ktor application to display more detailed debugging information in the IDE, such as call-stack. It will help with development and diagnosing issues.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;resources/application.conf&lt;/code&gt; file and add &lt;code&gt;development = true&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;ktor {
    development = true

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add Dependencies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Vonage Java SDK
&lt;/h3&gt;

&lt;p&gt;The Kotlin language provides &lt;a href="https://kotlinlang.org/docs/reference/java-interop.html"&gt;interoperability with Java&lt;/a&gt;, which allows you to call Java code from Kotlin code so that you can use &lt;a href="https://github.com/Vonage/vonage-java-sdk"&gt;Vonage Java SDK&lt;/a&gt; for the Kotlin/Ktor project.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;build.gradle&lt;/code&gt; file and add the following dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'com.vonage:client:6.1.0'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Serialization
&lt;/h3&gt;

&lt;p&gt;You will use JSON as a data format to communicate with the clients. You will serialize Kotlin objects using &lt;a href="https://github.com/Kotlin/kotlinx.serialization"&gt;Kotlin serialization&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;build.gradle&lt;/code&gt; file and add the following dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"io.ktor:ktor-serialization:$ktor_version"&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Kotlin serialization library uses preprocessing (at compile time), so you have to add the &lt;code&gt;org.jetbrains.kotlin.plugin.serialization&lt;/code&gt; Gradle plugin. At the time of writing this article, Ktor is using &lt;a href="https://youtrack.jetbrains.com/issue/KTOR-1620"&gt;using the old way of applying Gradle plugins&lt;/a&gt;, so we have to replace it with the new configuration.&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;build.gradle&lt;/code&gt; file and remove plugins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="nl"&gt;plugin:&lt;/span&gt; &lt;span class="s1"&gt;'kotlin'&lt;/span&gt;
&lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="nl"&gt;plugin:&lt;/span&gt; &lt;span class="s1"&gt;'application'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove the &lt;code&gt;mainClassName&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;mainClassName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"io.ktor.server.netty.EngineMain"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove the &lt;code&gt;classpath&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;classpath&lt;/span&gt; &lt;span class="s2"&gt;"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add plugins using the new Gradle syntax, just below &lt;code&gt;buildscript&lt;/code&gt; block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;buildscript&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s2"&gt;"java"&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s2"&gt;"org.jetbrains.kotlin.jvm"&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s2"&gt;"$kotlin_version"&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s2"&gt;"org.jetbrains.kotlin.plugin.serialization"&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s2"&gt;"$kotlin_version"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After all the modifiations, the &lt;code&gt;build.gradle&lt;/code&gt; file should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;buildscript&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;repositories&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;jcenter&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;classpath&lt;/span&gt; &lt;span class="s2"&gt;"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s2"&gt;"java"&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s2"&gt;"org.jetbrains.kotlin.jvm"&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s2"&gt;"$kotlin_version"&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s2"&gt;"org.jetbrains.kotlin.plugin.serialization"&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s2"&gt;"$kotlin_version"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="s1"&gt;'com.example'&lt;/span&gt;
&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s1"&gt;'0.0.1'&lt;/span&gt;

&lt;span class="n"&gt;sourceSets&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;kotlin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;srcDirs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;srcDirs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'src'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;kotlin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;srcDirs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;srcDirs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'test'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;srcDirs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'resources'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;srcDirs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'testresources'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;repositories&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mavenLocal&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;jcenter&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"io.ktor:ktor-server-netty:$ktor_version"&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"ch.qos.logback:logback-classic:$logback_version"&lt;/span&gt;
    &lt;span class="n"&gt;testImplementation&lt;/span&gt; &lt;span class="s2"&gt;"io.ktor:ktor-server-tests:$ktor_version"&lt;/span&gt;

    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'com.vonage:client:6.1.0'&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"io.ktor:ktor-serialization:$ktor_version"&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;kotlin_version&lt;/code&gt; and &lt;code&gt;ktor_version&lt;/code&gt; properties are defined inside &lt;code&gt;gradle.properties&lt;/code&gt; file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To enable serializatin, the JSON Converter has to be enabled for the Ktor application. Open the &lt;code&gt;Application.kt&lt;/code&gt; file and add an &lt;code&gt;install&lt;/code&gt; block inside &lt;code&gt;Application.module&lt;/code&gt; function:&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="nc"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ContentNegotiation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The IDE will mark all classes and extensions that have import missing with the red color. Rollover on the class or method name, wait for a window to appear, and select &lt;code&gt;import...&lt;/code&gt; to add class import and fix the error.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Create a Vonage Application
&lt;/h2&gt;

&lt;p&gt;A Vonage application will provide 2FA capabilities for the API. Create a Vonage application in the &lt;a href="https://dashboard.nexmo.com/applications"&gt;dashboard&lt;/a&gt;. Click the &lt;em&gt;Create a new application&lt;/em&gt; button, enter a name, and click the &lt;em&gt;Generate new application&lt;/em&gt; button.&lt;/p&gt;

&lt;p&gt;Go to &lt;a href="https://dashboard.nexmo.com/settings"&gt;settings&lt;/a&gt; and make a note of &lt;code&gt;API key&lt;/code&gt; and &lt;code&gt;API secret&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialize Vonage Client
&lt;/h2&gt;

&lt;p&gt;Add the &lt;code&gt;client&lt;/code&gt; property inside &lt;code&gt;Application.module&lt;/code&gt; function to initialize a Vonage client:&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="nc"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&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;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;VonageClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VonageClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"API_KEY"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apiSecret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"API_SECRET"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ContentNegotiation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;API_KEY&lt;/code&gt; and &lt;code&gt;API_SECRET&lt;/code&gt; using the values from the &lt;a href="https://dashboard.nexmo.com/settings"&gt;dashboard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;NOTE: in production  &lt;code&gt;API_KEY&lt;/code&gt; and &lt;code&gt;API_SECRET&lt;/code&gt; shuld be retrieved from &lt;a href="https://en.wikipedia.org/wiki/Environment_variable"&gt;environment variables&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  API Functionality
&lt;/h2&gt;

&lt;p&gt;You will build two API endpoints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;verifyNumber&lt;/code&gt; - the client will first hit this endpoint to start the verification process by processing the phone number to be verified. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;verifyCode&lt;/code&gt; - after receiving code (via SMS or voice call), the client will send the code, and the application will perform a 2FA check to determine if the client is verified.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Create verifyNumber API Endpoint
&lt;/h3&gt;

&lt;p&gt;Define a new route handler, &lt;code&gt;get("/verifyNumber")&lt;/code&gt;, inside the &lt;code&gt;routing&lt;/code&gt; block of the &lt;code&gt;Application.module&lt;/code&gt; function:&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="nc"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="nf"&gt;routing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;get&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="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2FA app is working"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ContentType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/verifyNumber"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The code within the &lt;code&gt;get("/verifyNumber")&lt;/code&gt; route handler will be executed when the client makes a call to the &lt;code&gt;http://localhost:8080/verifyNumber&lt;/code&gt; URL.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;verifyNumber&lt;/code&gt; endpoint will contain the following logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;retrieve &lt;code&gt;phoneNumber&lt;/code&gt; parameter from the query string (&lt;code&gt;http://localhost:8080/verifyNumber?phoneNumber=1234&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;start 2FA verification using the Vonage SDK&lt;/li&gt;
&lt;li&gt;return &lt;code&gt;requestId&lt;/code&gt; as a JSON (in a production application, you would typically store ID on the server-side)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add the following logic to the &lt;code&gt;get("/verifyNumber")&lt;/code&gt; route handler:&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/verifyNumber"&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;phoneNumber&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"phoneNumber"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;phoneNumber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNullOrBlank&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"phoneNumber is missing"&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;ongoingVerify&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verifyClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phoneNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"VONAGE"&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;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VerifyNumberResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ongoingVerify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&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;Define a &lt;code&gt;VerifyNumberResponse&lt;/code&gt; class that will be serialized to JSON and returned to the API client. Add the following code at the end of &lt;code&gt;Application.kt&lt;/code&gt; file:&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;@Serializable&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;VerifyNumberResponse&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;requestId&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;blockquote&gt;
&lt;p&gt;Kotlin allows defining multiple top-level members (classes, properties, etc.) within a single file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Due to a &lt;a href="https://youtrack.jetbrains.com/issue/KT-30161"&gt;bug&lt;/a&gt; in the Kotlin plugin, you need to add the import statement for &lt;code&gt;Serializable&lt;/code&gt; annotation manually. Add the following code at the top of the file, just below the last import statement:&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;import&lt;/span&gt; &lt;span class="nn"&gt;kotlinx.serialization.Serializable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Instead of using Vonage build-in verification, you could generate the code by yourself and send an SMS using Vonage Java SDK. However, the Vonage verification mechanism provides an easy way to use more complex &lt;a href="https://developer.nexmo.com/verify/guides/workflows-and-events"&gt;workflows&lt;/a&gt;, e.g.: default workflow will make a phone call and read the code to the user if the client did not provide SMS code within a specific period.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Create verifyCode API Endpoint
&lt;/h3&gt;

&lt;p&gt;Define a new route handler, &lt;code&gt;get("/verifyCode")&lt;/code&gt;, inside the &lt;code&gt;routing&lt;/code&gt; block of the &lt;code&gt;Application.module&lt;/code&gt; function:&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="nc"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="nf"&gt;routing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
        &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/verifyCode"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;verifyCode&lt;/code&gt; endpoint will contain the following logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;retrieve &lt;code&gt;code&lt;/code&gt; parameter from the query string (&lt;code&gt;code&lt;/code&gt; will be delivered to the user after hitting the &lt;code&gt;verifyNumber&lt;/code&gt; endpoint)&lt;/li&gt;
&lt;li&gt;retrieve a verification &lt;code&gt;requestId&lt;/code&gt; parameter from the query string (value retrieved from &lt;code&gt;verifyNumber&lt;/code&gt; endpoint)&lt;/li&gt;
&lt;li&gt;verify code using Vonage SDK&lt;/li&gt;
&lt;li&gt;return verification status to the client&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add the following logic to the &lt;code&gt;get("/verifyCode")&lt;/code&gt; route handler:&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/verifyCode"&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;code&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"code"&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;requestId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"requestId"&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;checkResponse&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verifyClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;code&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;checkResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&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;status&lt;/span&gt; &lt;span class="p"&gt;=&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;checkResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;VerifyStatus&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="s"&gt;"OK"&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="s"&gt;"ERROR: ${checkResponse.status}"&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;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VerifyCodeResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&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;Define a &lt;code&gt;VerifyCodeResponse&lt;/code&gt; class that will be serialized to JSON and returned to the API client. Add the following code at the end of &lt;code&gt;Application.kt&lt;/code&gt; file:&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;@Serializable&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;VerifyCodeResponse&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;status&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;p&gt;After all the modifications, &lt;code&gt;Application.kt&lt;/code&gt; file should look 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;package&lt;/span&gt; &lt;span class="nn"&gt;com.example&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.vonage.client.VonageClient&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.vonage.client.verify.VerifyStatus&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.ktor.application.*&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.ktor.features.*&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.ktor.http.*&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.ktor.response.*&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.ktor.routing.*&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.ktor.serialization.*&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;kotlinx.serialization.Serializable&lt;/span&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="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&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="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ktor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;netty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;EngineMain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@Suppress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unused"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Referenced in application.conf&lt;/span&gt;
&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jvm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;JvmOverloads&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&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;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;VonageClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VonageClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"API_KEY"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apiSecret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"API_KEY"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ContentNegotiation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;routing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;get&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="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2FA app is working"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ContentType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/verifyNumber"&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;phoneNumber&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"phoneNumber"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;phoneNumber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNullOrBlank&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"phoneNumber is missing"&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;ongoingVerify&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verifyClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phoneNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"VONAGE"&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;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VerifyNumberResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ongoingVerify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/verifyCode"&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;code&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"code"&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;requestId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"requestId"&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;checkResponse&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verifyClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;code&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;checkResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&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;status&lt;/span&gt; &lt;span class="p"&gt;=&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;checkResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;VerifyStatus&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="s"&gt;"OK"&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="s"&gt;"ERROR: ${checkResponse.status}"&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;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VerifyCodeResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&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="nd"&gt;@Serializable&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;VerifyNumberResponse&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;requestId&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="nd"&gt;@Serializable&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;VerifyCodeResponse&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;status&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;h2&gt;
  
  
  Use the API
&lt;/h2&gt;

&lt;p&gt;The API implementation is complete, so let's test it. &lt;/p&gt;

&lt;p&gt;Any client can use the API, including desktop and mobile clients, but you will perform simple testing by using a web browser.&lt;/p&gt;

&lt;p&gt;Launch the Ktor application.&lt;/p&gt;

&lt;p&gt;Replace &lt;code&gt;PHONE_NUMBER&lt;/code&gt; with an actual phone number and open the following URL in the browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8080/verifyNumber?phoneNumber=PHONE_NUMBER
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Vonage phone numbers are in &lt;a href="https://developer.nexmo.com/concepts/guides/glossary#e-164-format"&gt;E.164&lt;/a&gt; format, '+' and '-' are not valid. Make sure you specify your country code when entering your number, for example, US: 14155550100 and UK: 447700900001&lt;/p&gt;

&lt;p&gt;&lt;a href="https://help.nexmo.com/hc/en-us/articles/204014853-Nexmo-trial-period-How-to-add-numbers-to-list-of-permitted-destinations"&gt;As a trial user&lt;/a&gt;, you will only be able to send SMS and make voice calls to the number you registered with and up to 4 other test numbers of your choice (you can top up your Vonage account to remove this restriction).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You should receive an SMS with a code and see a similar response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"requestId":"9ac76db7971b4ea4a49f2e061432c6fe"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compose a second request. Replace &lt;code&gt;REQUEST_ID&lt;/code&gt;  with the value returned from server (in the above example, it's &lt;code&gt;9ac76db7971b4ea4a49f2e061432c6fe&lt;/code&gt;) and replace &lt;code&gt;CODE&lt;/code&gt; with the received verification code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8080/verifyCode?requestId=REQUEST_ID&amp;amp;code=CODE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the client phone number is verified, you should see the following response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"status":"OK"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You are using a default Vonage verification workflow (&lt;a href="https://developer.nexmo.com/verify/guides/workflows-and-events"&gt;https://developer.nexmo.com/verify/guides/workflows-and-events&lt;/a&gt;), so if you do not enter the code within 125 seconds, you will receive the voice call reading the code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;You can find the code shown in this tutorial on the &lt;a href="https://github.com/nexmo-community/ktor-2fa-server"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Below are a few other tutorials we've written either involving using our services with Go:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Vonage/vonage-java-sdk"&gt;Vonage Java SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.vonage.com/"&gt;Vonage API Developer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kotlinlang.org/"&gt;Kotlin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ktor.io/docs/welcome.html"&gt;Ktor docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kotlinlang.org/docs/reference/serialization.html"&gt;Kotlin Serialization&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have any questions, advice, or ideas you'd like to share with the community, please feel free to jump on our &lt;a href="https://developer.nexmo.com/community/slack"&gt;Community Slack workspace&lt;/a&gt;. I'd love to hear back from anyone that has implemented this tutorial and how your project works.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>ktor</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Right Way of Using Gradle Dynamic Dependencies</title>
      <dc:creator>Igor Wojda</dc:creator>
      <pubDate>Wed, 04 Nov 2020 15:14:31 +0000</pubDate>
      <link>https://dev.to/vonagedev/the-right-way-of-using-gradle-dynamic-dependencies-2671</link>
      <guid>https://dev.to/vonagedev/the-right-way-of-using-gradle-dynamic-dependencies-2671</guid>
      <description>&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%2Fi%2F6ton1m1zxc28oxc7smr0.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%2Fi%2F6ton1m1zxc28oxc7smr0.png" alt="Alt Text" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gradle.org/" rel="noopener noreferrer"&gt;Gradle&lt;/a&gt; is quite a popular and flexible build system. Among other things, it allows us to deal with dependency management. We can define all dependencies (external frameworks and libraries) that our application will use, e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
    implementation 'com.nexmo.android:client-sdk:2.7.0'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code will download a fixed 2.7.0 version of the &lt;a href="https://developer.nexmo.com/client-sdk/overview" rel="noopener noreferrer"&gt;Nexmo Client SDK&lt;/a&gt;. Before moving to dynamic dependencies, let’s take a quick look at semantic versioning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Semantic Versioning
&lt;/h2&gt;

&lt;p&gt;Most libraries nowadays follow &lt;a href="https://semver.org/" rel="noopener noreferrer"&gt;Semantic versioning specification&lt;/a&gt;, also known as semver. It promotes major.minor.patch (2.7.0) format for dependency versioning. Each number is incremented when particular criteria are met:&lt;br&gt;&lt;br&gt;
– &lt;code&gt;major&lt;/code&gt; part is incremented when backward-incompatible API changes are made. Pulling a new version will most likely break your build, e.g. method signature change or method removal.&lt;br&gt;&lt;br&gt;
– &lt;code&gt;minor&lt;/code&gt; part is incremented when functionality is added in a backward-compatible manner or improvements are introduced within the private code, e.g. new method is added to the API.&lt;br&gt;&lt;br&gt;
– &lt;code&gt;patch&lt;/code&gt; part is incremented when backward-compatible bug fixes are made. This is the most common type of release, and usually, we want to have the newest path version due to various bug fixes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Dynamic Dependencies
&lt;/h2&gt;

&lt;p&gt;Gradle also allows you to define a dynamic version of the dependency (library) using the &lt;code&gt;+&lt;/code&gt; character in the dependency definition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
    implementation 'com.nexmo.android:client-sdk:2.7.+'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example, Gradle will download client-sdk with the newest &lt;code&gt;path&lt;/code&gt; version, eg. 2.7.1, 2.7.2, 2.7.3, and so on. Gradle allows us to define dynamic versions retrieval in multiple ways. Let’s look at a few examples:&lt;br&gt;&lt;br&gt;
– implementation ‘com.nexmo.android:client-sdk:2.7.+’ – download new library version when &lt;code&gt;path&lt;/code&gt; version has changed&lt;br&gt;&lt;br&gt;
– implementation ‘com.nexmo.android:client-sdk:2.+’ – download new library version when &lt;code&gt;minor&lt;/code&gt; or &lt;code&gt;path&lt;/code&gt; versions has changed&lt;br&gt;&lt;br&gt;
– implementation ‘com.nexmo.android:client-sdk:+’ – always download newest version of the library&lt;/p&gt;

&lt;p&gt;This mechanism is a good way to ensure that we are using a library version with most up to date with all the performance improvements, patches, and bug fixes. However, in practice, the usage of dynamic dependencies may lead to multiple problems. Let’s consider a few real-life scenarios where the uses of dynamic dependencies have serious downsides.&lt;/p&gt;
&lt;h2&gt;
  
  
  Problematic Scenario 1
&lt;/h2&gt;

&lt;p&gt;Let’s start with the simplest scenario. Our application is using dynamic dependency for a 3rd party library. We discovered a bug in our application, and we know that it was working fine 1 month ago. We want to use one or more builds from the past to determine when bugs were introduced in our code base. We checked out code from the repository from one month ago, the moment in time when the application was working fine, and then we built the app, but the application still does not work as expected – the problem is still there. What happened? It turns out that the bug was in the external library. Our src code was from 30 days ago, but the Gradle dynamic dependency mechanism downloaded the newest version of dependency (with the bug) that was not present 1 month ago when the initial build was created (the one without a bug).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Lesson 1: It is hard to make the same build of the application when dynamic dependencies are used because the build depends on external library versions that most likely change over time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Problematic Scenario 2
&lt;/h2&gt;

&lt;p&gt;We are the creators of the library. We asked our users of our library explicitly to use this dynamic dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
    implementation 'com.nexmo.android:client-sdk:2.7.+’
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They have added this dependency to their project by merely copying the above code. At some point in time, we released a new version of the library that introduces a bug. Now users are reporting that “a few days ago, their application stopped working as expected.” Developers used a dynamic dependency version, unaware that the library’s new version causes the bug. Instead of quickly fixing the problem by reverting the library version, they send bug reports and wait for the fix.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Lesson 2: Do not advise your library users to use dynamic dependency versions because when you break the library, they will most likely not know the exact cause of the problem.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Solutions
&lt;/h2&gt;

&lt;p&gt;Modern IDEs like IntelliJ IDEA will warn us about dynamic dependency usage by usage:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nexmo.com/wp-content/uploads/2020/11/avoid-using-plus-character.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.nexmo.com%2Fwp-content%2Fuploads%2F2020%2F11%2Favoid-using-plus-character.png" alt="Avid using plus character" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To avoid unpredictability, we can specify dependency versions explicitly and update them manually when a need occurs. This will allow you to have full control over version updates and take a closer look at the dependency changelog and review the changes. IDE will also display a warning about outdated dependency and provides intention to change the version it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nexmo.com/wp-content/uploads/2020/11/change-dependency-version.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.nexmo.com%2Fwp-content%2Fuploads%2F2020%2F11%2Fchange-dependency-version.png" alt="Change dependency version" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This strategy works fine for small projects, but manual updates can be time-consuming when your project contains a large number of dependencies. We can use &lt;a href="https://github.com/ben-manes/gradle-versions-plugin" rel="noopener noreferrer"&gt;gradle-versions-plugin&lt;/a&gt; to determine how many dependencies need to be updated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./gradlew dependencyUpdates.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command will produce a report listing all the outdated dependencies.&lt;/p&gt;

&lt;p&gt;We can also take it one step further to balance deterministic builds and dynamic dependency versioning. &lt;a href="https://github.com/nebula-plugins/gradle-dependency-lock-plugin" rel="noopener noreferrer"&gt;Gradle Dependency Lock Plugin&lt;/a&gt; allows us to use dynamic dependency syntax in the Gradle build config and at the same time lock these dependencies to specific versions (overrides Gradle dynamic dependency behavior). This gives us full control of when dependencies should be updated. To update dependencies, we have to run a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./gradlew --refresh-dependencies generateLock saveLock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command will generate a &lt;code&gt;dependencies.lock&lt;/code&gt; file containing versions of dependencies. This file should be included in version control to ensure every developer in the team uses the same versions of dependencies. As a team, you can decide when to update dependencies, e.g. beginning of every development cycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Using Gradle dynamic dependencies may lead to new bugs in the application. If you are lucky, these bugs may be caught by tests, but this is not guaranteed. On top of that, the cause of the bug is often not obvious, leading to even more confusion. When building our application, we should decrease the number of moving parts. Ideally, builds should be deterministic and building the same source code should produce exactly the same application. If you plan to use the dynamic dependencies version, you should also use &lt;a href="https://github.com/nebula-plugins/gradle-dependency-lock-plugin" rel="noopener noreferrer"&gt;Gradle Dependency Lock Plugin&lt;/a&gt;; otherwise, you should specify your dependency versions explicitly.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.nexmo.com/blog/2020/11/04/the-right-way-of-using-gradle-dynamic-dependencies-dr" rel="noopener noreferrer"&gt;The Right Way of Using Gradle Dynamic Dependencies&lt;/a&gt; appeared first on &lt;a href="https://www.nexmo.com" rel="noopener noreferrer"&gt;Vonage Developer Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>developer</category>
      <category>tutorial</category>
      <category>android</category>
      <category>gradle</category>
    </item>
    <item>
      <title>Igor Wojda Joins the Vonage Developer Relations Team</title>
      <dc:creator>Igor Wojda</dc:creator>
      <pubDate>Wed, 11 Mar 2020 17:00:36 +0000</pubDate>
      <link>https://dev.to/vonagedev/igor-wojda-joins-the-vonage-developer-relations-team-54n</link>
      <guid>https://dev.to/vonagedev/igor-wojda-joins-the-vonage-developer-relations-team-54n</guid>
      <description>&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%2Fi%2Fgo61cvrvufn9fp04jde2.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%2Fi%2Fgo61cvrvufn9fp04jde2.png" alt="Alt Text" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My name is Igor and I am very excited to join Vonage as Android Developer Advocate.&lt;/p&gt;

&lt;p&gt;As a kid, I was always fascinated by computers. The mystery and infinite capacities always spiked my levels of curiosity. Now, many years later I am a Software Engineer with over 12 years of experience.&lt;/p&gt;

&lt;p&gt;I have been involved in many IT projects, building various applications across industries, organizations types, team sizes, methodologies, and technologies. Every project is unique having its own set of constraints, but one common characteristic is a neverending stream of emerging challenges—this is what I love about IT.&lt;/p&gt;

&lt;p&gt;At some point, I decided to share my knowledge and experience with the community, so I became a technical reviewer of the second Kotlin book ever published – &lt;a href="https://www.manning.com/books/kotlin-in-action" rel="noopener noreferrer"&gt;Kotlin In Action&lt;/a&gt; and a co-author of &lt;a href="https://www.packtpub.com/gb/application-development/android-development-kotlin" rel="noopener noreferrer"&gt;Android Development with Kotlin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On top of this, I am inspiring developers by writing &lt;a href="https://medium.com/@igorwojda" rel="noopener noreferrer"&gt;articles&lt;/a&gt;, speaking at conferences and contributing to &lt;a href="http://github.com/igorwojda" rel="noopener noreferrer"&gt;open-source projects&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nowadays I am specializing in mobile development. Android is my platform and Kotlin is my language of choice. However, my personal favorite topic is the &lt;a href="https://github.com/igorwojda/android-showcase" rel="noopener noreferrer"&gt;application architecture&lt;/a&gt;. With so many moving parts designing high quality modular, scalable, testable architecture that will support an application’s growth can be quite challenging.&lt;/p&gt;

&lt;p&gt;At Vonage, I will be working closely with external developers using our &lt;a href="https://developer.nexmo.com/client-sdk/sdk-documentation/android/release-notes" rel="noopener noreferrer"&gt;SDK&lt;/a&gt;. My goal is to improve the developer experience by writing high-quality documentation, building sample projects, writing tutorials and polishing the Client SDK API.&lt;/p&gt;

&lt;p&gt;I want to make the SDK not only more intuitive, easier to use but also more flexible and convenient to integrate into modern Android applications.&lt;/p&gt;

&lt;p&gt;Follow me on twitter to stay in touch &lt;a href="https://twitter.com/igorwojda" rel="noopener noreferrer"&gt;@igorwojda&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://www.nexmo.com/blog/2020/03/11/igor-wojda-joins-the-vonage-developer-relations-team-dr" rel="noopener noreferrer"&gt;Igor Wojda Joins the Vonage Developer Relations Team&lt;/a&gt; appeared first on &lt;a href="https://www.nexmo.com" rel="noopener noreferrer"&gt;Vonage Developer Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>developer</category>
      <category>android</category>
      <category>developeradvocates</category>
      <category>team</category>
    </item>
  </channel>
</rss>
