<?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: Vishesh</title>
    <description>The latest articles on DEV Community by Vishesh (@vishesh).</description>
    <link>https://dev.to/vishesh</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%2F299863%2F7821cff2-e7b1-4c67-a654-13497d03a65e.png</url>
      <title>DEV Community: Vishesh</title>
      <link>https://dev.to/vishesh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vishesh"/>
    <language>en</language>
    <item>
      <title>Spring AOP and Kotlin coroutines - What is wrong with Kotlin + SpringBoot</title>
      <dc:creator>Vishesh</dc:creator>
      <pubDate>Fri, 19 Sep 2025 04:52:32 +0000</pubDate>
      <link>https://dev.to/vishesh/learning-spring-aop-and-kotlin-coroutines-what-is-wrong-with-kotlin-springboot-5417</link>
      <guid>https://dev.to/vishesh/learning-spring-aop-and-kotlin-coroutines-what-is-wrong-with-kotlin-springboot-5417</guid>
      <description>&lt;p&gt;Are you using Springboot and Kotlin? Have you heard about spring AOP? If not then some of spring annotations like @HandlerInterceptor, @Retryable may not work as you except.&lt;/p&gt;

&lt;h2&gt;
  
  
  What?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  AOP Proxies and Suspending Functions:
&lt;/h3&gt;

&lt;p&gt;Spring AOP primarily relies on creating proxies around your beans. When you apply an aspect to a method, Spring creates a proxy that intercepts calls to that method and applies the advice (e.g., @Around, @Before, @After). This mechanism generally works for suspending functions as well. But when using AOP based annotations. We must exercise caution and test them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Spring creates proxies?
&lt;/h3&gt;

&lt;p&gt;Proxies are for cross-cutting concerns:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Web Request Handling - HTTP request/response processing, validating request, serialising and deserialising request and response, processing headers, so on ...&lt;/li&gt;
&lt;li&gt;Transaction Management - Begin/commit/rollback&lt;/li&gt;
&lt;li&gt;Security - Authorization checks&lt;/li&gt;
&lt;li&gt;Caching - Store/retrieve cached results&lt;/li&gt;
&lt;li&gt;Retry Logic - Retry failed operations&lt;/li&gt;
&lt;li&gt;Logging/Monitoring - Method entry/exit tracking&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Example of commonly used proxy enabled Spring components:&lt;/strong&gt;&lt;br&gt;
@RestController // Creates proxy for web-related concerns&lt;br&gt;
@Transactional // Database transaction management&lt;br&gt;
@Cacheable // Caching&lt;br&gt;
&lt;a class="mentioned-user" href="https://dev.to/async"&gt;@async&lt;/a&gt; // Asynchronous execution&lt;br&gt;
@Secured // Security&lt;br&gt;
@PreAuthorize // Security authorization&lt;br&gt;
@Retryable // Retry logic&lt;/p&gt;
&lt;h4&gt;
  
  
  Reason why AOP does not work well with co routines is: Fundamental Architecture Mismatch: AOP proxies operate at method call level, coroutines operate at language/compiler level
&lt;/h4&gt;
&lt;h2&gt;
  
  
  Below are commonly used annotations and how to properly use them in kotlin.
&lt;/h2&gt;
&lt;h3&gt;
  
  
  @Transactional
&lt;/h3&gt;

&lt;p&gt;This is supported as per recent update. Maybe your LLM like chatgpt and others maynot have the latest context. So do not believe it, here is the closed github issue &lt;a href="https://github.com/spring-projects/spring-framework/issues/24226" rel="noopener noreferrer"&gt;https://github.com/spring-projects/spring-framework/issues/24226&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  @Retryable
&lt;/h3&gt;

&lt;p&gt;The spring's interceptor AOP will be unable to process the suspended function and catch its exceptions to retry. Best way is to add a custom retry mechanism. Below is sample of what works. this is testable and no need to mock during tests as well.&lt;br&gt;
&lt;strong&gt;Note:&lt;/strong&gt; But do not create a custom spring annotation again here as it will again lead to using spring AOPs and the logic wont work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * Utility class for retry operations with configurable backoff strategy.
 */
public class Retry {
    /**
     * Executes a block of code with retry logic and configurable backoff.
     *
     * @param &amp;lt;T&amp;gt;            The return type of the block.
     * @param maxAttempts    Maximum number of retry attempts.
     * @param initialDelayMs Initial delay in milliseconds.
     * @param maxDelayMs     Maximum delay cap in milliseconds.
     * @param backoffFactor  Multiplier for exponential backoff (default is 1.0 - no backoff).
     * @param retryOn        List of exception types that should trigger retry.
     * @param block          The block to execute with retry logic.
     * @return The result of the block if successful.
     * @throws Exception If all retries fail or a non-retryable exception is thrown.
     */
    public static &amp;lt;T&amp;gt; T withRetry(
            int maxAttempts,
            long initialDelayMs,
            long maxDelayMs,
            double backoffFactor,
            List&amp;lt;Class&amp;lt;? extends Throwable&amp;gt;&amp;gt; retryOn,
            Callable&amp;lt;T&amp;gt; block
    ) throws Exception {
        int attempts = 0;
        Throwable lastException = null;
        long currentDelay = initialDelayMs;

        while (attempts &amp;lt; maxAttempts) {
            try {
                return block.call();
            } catch (Throwable e) {
                boolean shouldRetry = false;
                for (Class&amp;lt;? extends Throwable&amp;gt; retryClass : retryOn) {
                    if (retryClass.isInstance(e)) {
                        shouldRetry = true;
                        break;
                    }
                }
                if (shouldRetry) {
                    lastException = e;
                    attempts++;
                    if (attempts &amp;gt;= maxAttempts) {
                        break;
                    }
                    // Calculate exponential backoff
                    if (attempts &amp;gt; 1 &amp;amp;&amp;amp; backoffFactor &amp;gt; 1) {
                        currentDelay = calculateBackoffDelay(
                                initialDelayMs,
                                maxDelayMs,
                                backoffFactor,
                                attempts
                        );
                    }
                    try {
                        Thread.sleep(currentDelay);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        throw ie;
                    }
                } else {
                    // For exceptions we throw immediately, no retry
                    throw e;
                }
            }
        }
        if (lastException instanceof Exception) {
            throw (Exception) lastException;
        } else if (lastException != null) {
            throw new RuntimeException(lastException);
        } else {
            throw new IllegalStateException("Retry failed for unknown reason");
        }
    }

    /**
     * Calculate delay with exponential backoff.
     * Example:
     * With maxAttempts = 3, initialDelayMs = 100ms and backoffFactor = 2.0:
     * Retry Attempt 1: 100ms (initial delay)
     * Retry Attempt 2: 100 * (2^1) = 200ms
     * Retry Attempt 3: No delay as max attempt reached
     */
    private static long calculateBackoffDelay(
            long initialDelayMs,
            long maxDelayMs,
            double backoffFactor,
            int attempt
    ) {
        long exponentialDelay = (long) (initialDelayMs * Math.pow(backoffFactor, attempt - 1));
        // Cap at maximum delay
        return Math.min(exponentialDelay, maxDelayMs);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  @HandlerInterceptor
&lt;/h3&gt;

&lt;p&gt;The spring's interceptor AOP will be unable to process the suspended controller function. This will make the controller return coroutine object as response but meanwhile the actual controller logic will be running on the background. Hence, the client will receive empty or wrong response.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9t529niolhufrg6pgy7r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9t529niolhufrg6pgy7r.png" alt="This image explain what happens if we use regular @HandlerInterceptor" width="787" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To circumvent above issue. The best way is to use &lt;code&gt;CoWebFilter&lt;/code&gt;. This filter is applied same as handler. It can handle request and response. Below is a sample implementation.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HeaderInterceptor&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CoWebFilter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// Filter runs BEFORE any controller is involved&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ServerWebExchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CoWebFilterChain&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Verify requests details&lt;/span&gt;

        &lt;span class="c1"&gt;// Decorate the response to capture it for any processing&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;decoratedExchange&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;decorateExchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idempotencyKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;// proceed to the controller        &lt;/span&gt;
        &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decoratedExchange&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;decorateExchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ServerWebExchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;idempotencyKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;ServerWebExchange&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;decoratedResponse&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
      &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="err"&gt;: &lt;/span&gt;&lt;span class="nc"&gt;ServerHttpResponseDecorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exchange&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;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;writeWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Publisher&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="nc"&gt;DataBuffer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;):&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// Read the body and cache it&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;DataBufferUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatMap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;dataBuffer&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
              &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;bytes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ByteArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readableByteCount&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
              &lt;span class="n"&gt;dataBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="nc"&gt;DataBufferUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataBuffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

              &lt;span class="nf"&gt;mono&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Add your own logic to save or modify the response body and status code&lt;/span&gt;
                &lt;span class="c1"&gt;// response data is available as `bytes`. you can convert to String or DTO&lt;/span&gt;
              &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

              &lt;span class="c1"&gt;// Write the original response body&lt;/span&gt;
              &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;just&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                  &lt;span class="n"&gt;exchange&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="nf"&gt;bufferFactory&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&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="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Return a new exchange with the decorated response&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decoratedResponse&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>springboot</category>
      <category>kotlin</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>API Idempotency: Why Your System Needs It?</title>
      <dc:creator>Vishesh</dc:creator>
      <pubDate>Fri, 19 Sep 2025 03:52:45 +0000</pubDate>
      <link>https://dev.to/vishesh/api-idempotency-why-your-system-needs-it-32k8</link>
      <guid>https://dev.to/vishesh/api-idempotency-why-your-system-needs-it-32k8</guid>
      <description>&lt;h2&gt;
  
  
  API Idempotency: Why Your System Needs It?
&lt;/h2&gt;

&lt;p&gt;In any system, there are processes that cannot be duplicated and the system must be resilient to duplicate requests. It's better to prevent issues rather than correct them later down the line.&lt;/p&gt;

&lt;p&gt;Clients retry failed requests. Your system processes them multiple times. Data gets corrupted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Client: "Add 100 units to inventory"&lt;/li&gt;
&lt;li&gt;Server: Adds it&lt;/li&gt;
&lt;li&gt;Client: Network fails, hence retries&lt;/li&gt;
&lt;li&gt;Server: Adds 100 units... twice&lt;/li&gt;
&lt;li&gt;Result: 200 units instead of 100 ❌&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solution: Idempotency header
&lt;/h2&gt;

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

&lt;h3&gt;
  
  
  Client responsibilities:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create unique idempotency key for each request&lt;/li&gt;
&lt;li&gt;Re-use same idempotency key for any retry attempts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Server responsibilities:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If idempotency key is given, save the successful responses. Only success 2XX responses because they are the only ones that update the main DB data. Other 4XX, 5XX do not change the DB.&lt;/li&gt;
&lt;li&gt;If idempotency key is not given*&lt;em&gt;,&lt;/em&gt;* process as usual as it's the client*&lt;em&gt;'s&lt;/em&gt;* responsibility to add idempotency for critical operations&lt;/li&gt;
&lt;li&gt;Do not save idempotency data for GET HTTP APIs as they only retrieve data and do not update any data.&lt;/li&gt;
&lt;li&gt;Based on the need*&lt;em&gt;,&lt;/em&gt;* add a scheduled job to remove the idempotency data. E.g.: Delete records older than 7 days&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Sample DB (Postgres) design to store the response:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Column&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Nullable&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;id&lt;/td&gt;
&lt;td&gt;int8 (Primary Key)&lt;/td&gt;
&lt;td&gt;False&lt;/td&gt;
&lt;td&gt;Auto Increment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;idempotency_key&lt;/td&gt;
&lt;td&gt;VARCHAR(50)&lt;/td&gt;
&lt;td&gt;False&lt;/td&gt;
&lt;td&gt;NA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;http_status_code&lt;/td&gt;
&lt;td&gt;VARCHAR(50)&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;NA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;http_response&lt;/td&gt;
&lt;td&gt;TEXT&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;td&gt;NA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;created_at&lt;/td&gt;
&lt;td&gt;TIMESTAMP&lt;/td&gt;
&lt;td&gt;False&lt;/td&gt;
&lt;td&gt;CURRENT_TIMESTAMP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Sample implementation (API):
&lt;/h3&gt;

&lt;p&gt;Interceptor&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleIdempotencyInterceptor&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;HandlerInterceptor&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;IdempotencyDataRepositoryImpl&lt;/span&gt; &lt;span class="n"&gt;idempotencyRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Before sending request to the controller we can check the headers here&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;preHandle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;HttpServletRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;HttpServletResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Skip GET requests and non-configured paths&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMethod&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpMethod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;GET&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isPathEnabled&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;idempotencyKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getHeader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"myapp-idempotency-key"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idempotencyKey&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;handleIdempotency&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idempotencyKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Save the response after receiving form the controller&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterCompletion&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;HttpServletRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;HttpServletResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;idempotencyKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getHeader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"myapp-idempotency-key"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idempotencyKey&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStatus&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStatus&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;299&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Cache successful response&lt;/span&gt;
            &lt;span class="n"&gt;cacheResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idempotencyKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;handleIdempotency&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;idempotencyKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HttpServletResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;IdempotencyDataEntity&lt;/span&gt; &lt;span class="n"&gt;existing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;idempotencyRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findByIdempotencyKey&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idempotencyKey&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;existing&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"null"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"exists"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"null"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="c1"&gt;// First time - create placeholder record&lt;/span&gt;
                &lt;span class="n"&gt;idempotencyRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IdempotencyDataEntity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;idempotencyKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                        &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                        &lt;span class="s"&gt;""&lt;/span&gt;
                    &lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="c1"&gt;// Return cached response&lt;/span&gt;
                &lt;span class="n"&gt;sendCachedResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;existing&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;cacheResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;idempotencyKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HttpServletResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;IdempotencyDataEntity&lt;/span&gt; &lt;span class="n"&gt;existing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;idempotencyRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findByIdempotencyKey&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;idempotencyKey&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;existing&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// ... response body reading logic would go here ...&lt;/span&gt;
                &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;responseBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/* extracted response body */"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

                &lt;span class="n"&gt;idempotencyRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IdempotencyDataEntity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;idempotencyKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStatus&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                        &lt;span class="n"&gt;responseBody&lt;/span&gt;
                    &lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;warn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to cache idempotency response for key: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;idempotencyKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scheduler for cleaning up old Idempotency data&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
     * Cleanup idempotency records older than 7 days
     * Runs daily at 2 AM
     */&lt;/span&gt;
    &lt;span class="nd"&gt;@Scheduled&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cron&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0 0 2 * * *"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;cleanupOldIdempotencyRecords&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt; &lt;span class="n"&gt;cutoffDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;minusDays&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;deletedCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;idempotencyRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deleteByCreatedDateBefore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cutoffDate&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cleaned up {} old idempotency records (older than {})"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deletedCount&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cutoffDate&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to cleanup old idempotency records"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>webdev</category>
      <category>programming</category>
      <category>api</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Import data(CSV) (Google Cloud Import API)</title>
      <dc:creator>Vishesh</dc:creator>
      <pubDate>Mon, 20 Jan 2025 06:33:50 +0000</pubDate>
      <link>https://dev.to/vishesh/import-datacsv-via-google-cloud-import-api-40ib</link>
      <guid>https://dev.to/vishesh/import-datacsv-via-google-cloud-import-api-40ib</guid>
      <description>&lt;p&gt;Recently in my SpringBoot application I was working on importing data from multiple CSV files into my GCS(Google Cloud) &lt;strong&gt;Postgress DB&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem statement
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The import operation will be triggered by a REST endpoint in SpringBoot.&lt;/li&gt;
&lt;li&gt;All the CSV files will be available in the &lt;strong&gt;GCS bucket&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A CSV file can contain &lt;strong&gt;1 to 10 million records&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;We will have to all the CSV files before proceeding to next task.&lt;/li&gt;
&lt;li&gt;The import operation &lt;strong&gt;status&lt;/strong&gt; should be recorded.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Below are the solutions I explored,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Method 1: COPY Command&lt;/strong&gt; - Perform PostgreSQL Import CSV Job using the COPY Command. We will have to download the file to the machine and then run the below query in the application.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emp_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="err"&gt;‘&lt;/span&gt;&lt;span class="k"&gt;C&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;newdbemployees&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="err"&gt;’&lt;/span&gt;
&lt;span class="k"&gt;DELIMITER&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;CSV&lt;/span&gt; &lt;span class="n"&gt;HEADER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzqu4pvfm87g0ba1i52p3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzqu4pvfm87g0ba1i52p3.png" alt="COPY Command flow diagram" width="357" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Method 2: Postgres CLI COPY command&lt;/strong&gt; - Use Postgres CLI COPY command. We will have to download the file to the machine and then run the below CLI query in the application
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Login */&lt;/span&gt;
&lt;span class="n"&gt;psql&lt;/span&gt; &lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5432&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;
&lt;span class="cm"&gt;/* Copy */&lt;/span&gt;
&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;COPY&lt;/span&gt; &lt;span class="n"&gt;employee_table&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="s1"&gt;'employees.csv'&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;CSV&lt;/span&gt; &lt;span class="n"&gt;HEADER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5bmnrps48qrafhavb5f3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5bmnrps48qrafhavb5f3.png" alt="Postgres CLI COPY flow diagram" width="356" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Method 3: Use &lt;a href="https://cloud.google.com/sql/docs/postgres/import-export/import-export-csv" rel="noopener noreferrer"&gt;GCP import API&lt;/a&gt;&lt;/strong&gt; - This might be little slow as import API is synchronous and only allows importing one file at a time.Only suitable if the import is straightforward and has no follow up operations on the imported data.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu8wyyw1fmk51asvyivmq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu8wyyw1fmk51asvyivmq.png" alt="GCP import API flow diagram" width="357" height="382"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Method 4: Stream file and save in DB&lt;/strong&gt; - This is the fastest. We can read the file as a stream and save each CSV line that we read in the database. We can leverage database batching to save multiple lines (ex: 1000) at a time. If we stream the file we need not download the full file locally. We can also stream the file asynchronously thus improving speed.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbrtkk9zwud2wob7b9y3y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbrtkk9zwud2wob7b9y3y.png" alt="Stream file and save in DB flow diagram" width="357" height="382"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Method 4 (Stream file and save in DB)&lt;/strong&gt; if you have to upload multiple files parallelly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Method 3 (GCS import API)&lt;/strong&gt; if you are have lesser files and uploading one by one synchronously is not a concern&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Disadvantages
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Method 1 &amp;amp; Method 2:
&lt;/h4&gt;

&lt;p&gt;We must do lot of heavy lifting and handle many error cases.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The files are present in the GCS bucket. Hence, to pass it to the command, I will have to download it first and keep it in the system memory. Which is not ideal.&lt;/li&gt;
&lt;li&gt;After importing is done, I must delete the file form the server. If any error occurs here then the system memory will increase.&lt;/li&gt;
&lt;li&gt;During the import operation my system memory and CPU usage will spike. Especially when the CSV files have millions of records.&lt;/li&gt;
&lt;li&gt;Accessing production psql is generally not recommended and has security concerns. It would lead to easier SQL query injection, etc...&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Method 3
&lt;/h4&gt;

&lt;p&gt;The main problem with &lt;strong&gt;GCS import API&lt;/strong&gt; is that its &lt;strong&gt;synchronous&lt;/strong&gt;. We can &lt;strong&gt;only run one import operation at one time per DB instance&lt;/strong&gt;. This is because postgres itself under hood only allows one COPY process to run at a given time. It is a necessary condition to avoid data inconsistency.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If one table is being updated by 2 different files then the order of data cannot be preserved.&lt;/li&gt;
&lt;li&gt;Also, it will be added overhead to rollback incase of failure when 2 or more files are being imported&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hence we have to ensure to wait for one file import to complete and then trigger another file.&lt;/p&gt;

&lt;h4&gt;
  
  
  Method 4
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;High CPU utilisation. Processing parallelly may need higher configuration of the system&lt;/li&gt;
&lt;li&gt;High database utilisation. Since we are saving line by line. When saving large files. Huge number of queries run. Hence we will need higher configuration of database&lt;/li&gt;
&lt;li&gt;Better error handling. Asynchronous processes are often difficult to handle, we need to plan for errors at multiple stages. Like Connecting to DB, thread locking, query fails, etc...&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>devops</category>
      <category>cloud</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>API logs - Best practices</title>
      <dc:creator>Vishesh</dc:creator>
      <pubDate>Mon, 13 Feb 2023 04:16:53 +0000</pubDate>
      <link>https://dev.to/vishesh/api-logs-best-practices-3ddp</link>
      <guid>https://dev.to/vishesh/api-logs-best-practices-3ddp</guid>
      <description>&lt;h3&gt;
  
  
  How to log information in APIs?
&lt;/h3&gt;

&lt;p&gt;Logging the right information will save you &lt;strong&gt;hours of debugging&lt;/strong&gt; and improve &lt;strong&gt;code flow visibility&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do not &lt;strong&gt;console.log or println&lt;/strong&gt; etc... Because,

&lt;ul&gt;
&lt;li&gt;These logs are directly added to the terminal where the server runs.&lt;/li&gt;
&lt;li&gt;This is difficult to read and record.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Use a library or create a separate service for logging. Better to save them in a separate file at a secure location if not using any existing libraries.&lt;/li&gt;
&lt;li&gt;Avoid logging sensitive data. Like,

&lt;ul&gt;
&lt;li&gt;usernames&lt;/li&gt;
&lt;li&gt;tokens&lt;/li&gt;
&lt;li&gt;password&lt;/li&gt;
&lt;li&gt;Database config data&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Avoid logging each stage of the API like, starting, started, processing, done. Instead add details like,

&lt;ul&gt;
&lt;li&gt;Non-sensitive but critical input data that determine the API flow. Ex: flags or reference ids.&lt;/li&gt;
&lt;li&gt;Log any third party integrated APIs. Ex: "Firebase Authentication response: 200 success", but do not log the actual response.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Make it as &lt;strong&gt;meaningful&lt;/strong&gt; as possible with limited data.&lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;header&lt;/strong&gt; for easy filtering. Ex: "Login API: ...." or "Get user details: ...&lt;/li&gt;
&lt;li&gt;Add enough &lt;strong&gt;facts&lt;/strong&gt; or information for debugging the issue. Not just plain sentences like as it will be easy to cross refer and confirm the understanding. Ex:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Avoid&lt;/strong&gt;: "Received response from firebase"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better&lt;/strong&gt;: "User authenticated by firebase: " or "Firebase authentication failed: "&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Add only suspicious or areas where logs could help. Ex: Third party integration - It will help decide if our code went wrong or third party API returned wrong.

&lt;ul&gt;
&lt;li&gt;Avoid adding in simple get APIs. Where there is no much data processing, only data retrieving. So, no need of detailed logging here.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Do not forget to remove when not necessary. It will save a log of time when viewing and filtering the logs.&lt;/li&gt;
&lt;li&gt;Do not add logs in core areas like (index.js, routes, middlewares, etc…)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Log types
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Error&lt;/strong&gt;: This is used when you need to log any error like DB connection failed or uncaught error thrown.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debug&lt;/strong&gt;: This is debugging logs. This is used to log any information that can will used for better understanding of the API flow. Ex: "Third party auth succeed".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Warn&lt;/strong&gt;: This is used when logging issues that may or suspected to cause issues. Like malformed data from any third party or any API header missing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log&lt;/strong&gt;: This is a general log type.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>logs</category>
      <category>api</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Software releases</title>
      <dc:creator>Vishesh</dc:creator>
      <pubDate>Thu, 02 Feb 2023 10:17:30 +0000</pubDate>
      <link>https://dev.to/vishesh/software-releases-26bg</link>
      <guid>https://dev.to/vishesh/software-releases-26bg</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;It depends .... 😜&lt;/p&gt;

&lt;p&gt;This is the common term in software industry. And for the most part its true. Unless you know about the goals of the software you cannot determine a release process. Generally there is a standard framework(that I observed working in 10+ projects):&lt;/p&gt;

&lt;p&gt;Note: the software here means both frontend and backend&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Split the software into small &lt;strong&gt;deliverable modules&lt;/strong&gt;. Ex: to build a motor cycle start by building a skateboard, then a cycle then a motor cycle. This way you can use and test each module.&lt;/li&gt;
&lt;li&gt;Build minimum &lt;strong&gt;3 environments&lt;/strong&gt; where the software can be deployed. ITs more like having same software running in 3 different servers and 3 domains

&lt;ul&gt;
&lt;li&gt;dev.mysoftware.com - Development environment&lt;/li&gt;
&lt;li&gt;stage.mysoftware.com - Staging / beta environment&lt;/li&gt;
&lt;li&gt;mysoftware.com - Production / main environment&lt;/li&gt;
&lt;li&gt;test.mysoftware.com - Temporary testing environment&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Each environment will have it's own code running in backend and frontend servers. Each environment code will be different and will be a different branch in the GitHub repository.&lt;/p&gt;

&lt;h4&gt;
  
  
  Development environment
&lt;/h4&gt;

&lt;p&gt;This is where developers can deploy all latest working code and release to testing.&lt;/p&gt;

&lt;h4&gt;
  
  
  Staging environment
&lt;/h4&gt;

&lt;p&gt;This is essentially a well tested copy of software with new features or modules waiting to be tested and approved by the stakeholders(Who own the software).&lt;/p&gt;

&lt;h4&gt;
  
  
  Production environment
&lt;/h4&gt;

&lt;p&gt;This is where fully approved software is deployed and used by the end users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing environment
&lt;/h3&gt;

&lt;p&gt;If only obsoletely necessary, we can have this environment to test any new things we try. Like DevOps terraform or AWS etc.. integrations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Release cycles
&lt;/h2&gt;

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

&lt;h3&gt;
  
  
  General code release process
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;When working on a module / feature create a feature branch from development branch and work in it.&lt;/li&gt;
&lt;li&gt;After completing feature development. Update the feature branch code in development branch. Normally we use PR (&lt;a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests" rel="noopener noreferrer"&gt;Pull requests&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Deploy to development environment.&lt;/li&gt;
&lt;li&gt;Initiate testing.&lt;/li&gt;
&lt;li&gt;Do bug fixes if there are any. Here as well create a bug fix branch and move the code to development using PRs.&lt;/li&gt;
&lt;li&gt;Post testing passed, move development code to staging branch.&lt;/li&gt;
&lt;li&gt;Deploy in staging environment.&lt;/li&gt;
&lt;li&gt;Perform a sanity to ensure all changes are working fine.&lt;/li&gt;
&lt;li&gt;If there are any bugs identified in sanity then fix them by creating a new hotfix branch from staging. Make sure to move these hotfix changes to development branch as well. &lt;/li&gt;
&lt;li&gt;Post completing sanity, move all changes in staging to production branch.&lt;/li&gt;
&lt;li&gt;Deploy production branch. [Do not do testing in production]&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Release with feature flags
&lt;/h3&gt;

&lt;p&gt;Post releasing a feature or change to production. Suppose, this new feature needs to be &lt;strong&gt;rolled back or put on hold&lt;/strong&gt; because of any issue, for example server / website performance is slow. Then our only way is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a hotfix in staging to remove this feature&lt;/li&gt;
&lt;li&gt;Test this change&lt;/li&gt;
&lt;li&gt;Do a bug fix if this change causes any issues&lt;/li&gt;
&lt;li&gt;Re-test if any bug fixes&lt;/li&gt;
&lt;li&gt;Move code to production.&lt;/li&gt;
&lt;li&gt;Repeat the same, from step 1 to enable this feature later on.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is very &lt;strong&gt;expensive&lt;/strong&gt; and includes &lt;strong&gt;multiple developing and testing cycles&lt;/strong&gt;. Can we avoid it? Oh yes, &lt;strong&gt;feature flags&lt;/strong&gt;. They are similar to environment variables. Each environment will have different values but the name will remain the same.&lt;/p&gt;

&lt;p&gt;The concept is to develop features with &lt;strong&gt;feature flag conditions&lt;/strong&gt; in place, in such a way that,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the flag value is &lt;strong&gt;true&lt;/strong&gt; then feature should be &lt;strong&gt;enabled&lt;/strong&gt; or shown in the software.&lt;/li&gt;
&lt;li&gt;If the flag value is &lt;strong&gt;false&lt;/strong&gt; then feature should be &lt;strong&gt;disabled&lt;/strong&gt; or should not be shown in the software.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hence, here if we need to rollback / switch off a feature, its very simple. We just need to set this flag as false and re-deploy or use a cloud third party like &lt;a href="https://launchdarkly.com/" rel="noopener noreferrer"&gt;LaunchDarkly&lt;/a&gt; and change the value to false.&lt;/p&gt;

&lt;p&gt;Once a feature is in production and working fine. We must &lt;strong&gt;remove&lt;/strong&gt; this feature flag conditions form code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Release with scientific experiment
&lt;/h3&gt;

&lt;p&gt;This is special case for microservices covered in this article &lt;a href="https://dev.to/vishesh/how-to-test-new-microservice-apis-monolith-to-microservice-2oi8"&gt;How to test new microservice APIs - (Monolith to Microservice)&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Few additional tips:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Think ahead of time and maintain a sheet with release check lists. It will help in future.&lt;/li&gt;
&lt;li&gt;Make sure that infra setup is working fine before deploying.&lt;/li&gt;
&lt;li&gt;Double check with environment variables.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>blockchain</category>
      <category>crypto</category>
      <category>web3</category>
    </item>
    <item>
      <title>Communication within microservices</title>
      <dc:creator>Vishesh</dc:creator>
      <pubDate>Thu, 02 Feb 2023 06:19:15 +0000</pubDate>
      <link>https://dev.to/vishesh/communication-within-microservices-31mn</link>
      <guid>https://dev.to/vishesh/communication-within-microservices-31mn</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Every microservice has its own bounded context and will not have data is not within it's domain. Lets assume, we have a ecommerce system with below microservices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User microservice - Includes APIs to login, log out and user profile&lt;/li&gt;
&lt;li&gt;Product microservice - Include APIs to list, add, edit or delete products&lt;/li&gt;
&lt;li&gt;Billing microservice - Include APIs to generate bill and initiate delivery.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why we need to communicate?
&lt;/h2&gt;

&lt;p&gt;If we observe closely, for billing microservice, to generate a bill it will need product price and details. Hence the billing microservice will need to communicate with product microservice and get the full product details.&lt;/p&gt;

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

&lt;p&gt;Simple, call the list API in product microservice from billing microservice. Yes, this should be fine and we are not violating the bounded context by accessing product details directly from DB.&lt;/p&gt;

&lt;h4&gt;
  
  
  But ...
&lt;/h4&gt;

&lt;p&gt;But we have to &lt;strong&gt;authenticate&lt;/strong&gt; right? Generally frontend will use the user login &lt;strong&gt;token&lt;/strong&gt; to authenticate. But how can a microservice get this token? It will not have the user login details. Moreover, this is user token and is meant to be used for APIs that a end user can access. So, its not a good practice to use this token to communicate between microservices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Hence we will need a specific token to authenticate communication between microservices. Token can be anything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JWT token&lt;/li&gt;
&lt;li&gt;Firebase token&lt;/li&gt;
&lt;li&gt;Custom token generation logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We must ensure to follow below process to ensure the communication is secure,&lt;/p&gt;

&lt;h4&gt;
  
  
  Solution 1
&lt;/h4&gt;

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

&lt;ul&gt;
&lt;li&gt;In the authentication mechanism for all APIs introduce a new API header like "X-MS-TOKEN". When ever this is passed decrypt and verify this token.&lt;/li&gt;
&lt;li&gt;In the billing microservice when calling product microservice for details. Encrypt and attach the token in the "X_MS_TOKEN" API header
This is a very simple way. But this has a &lt;strong&gt;security concern&lt;/strong&gt; that if this token is leaked then anyone can easily access the API. We are not refreshing this token at all. This could be a concern when dealing with APIs that involve billing. Anyone could easily generate multiple bills.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  For more security
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Use a third part service like firebase or create your own token generation service and fetch a token every time you call API to get details from other microservice. This way the token remain fresh each time and even if its leaked it not relevant for single use.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>blockchain</category>
      <category>web3</category>
      <category>crypto</category>
    </item>
    <item>
      <title>How to test new microservice APIs - (Monolith to Microservice)</title>
      <dc:creator>Vishesh</dc:creator>
      <pubDate>Thu, 02 Feb 2023 05:11:55 +0000</pubDate>
      <link>https://dev.to/vishesh/how-to-test-new-microservice-apis-monolith-to-microservice-2oi8</link>
      <guid>https://dev.to/vishesh/how-to-test-new-microservice-apis-monolith-to-microservice-2oi8</guid>
      <description>&lt;p&gt;This article will cover on a interesting approach to test newly build microservice APIs. Assuming that we are building a new microservice breaking an old monolith application that is already in production and used by end users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debrief
&lt;/h2&gt;

&lt;p&gt;We had a very large monolith and had to break them into multiple microservices. This was easy due to the &lt;a href="https://dev.to/vishesh/technical-design-document-microservice-200g"&gt;TDD&lt;/a&gt; practice. The problem was on deciding the release process. How will you release the microservice APIs without affecting the end users and also test then thoroughly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Release and test gradually and not at once&lt;/li&gt;
&lt;li&gt;Need a way to test the new microservice APIs before releasing&lt;/li&gt;
&lt;li&gt;Foresee any issues early before actual release&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Proposed solution
&lt;/h2&gt;

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

&lt;p&gt;Below is the list of steps how the experiment should work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend calls the new monolith API.&lt;/li&gt;
&lt;li&gt;In backend, the &lt;strong&gt;API middleware&lt;/strong&gt; is triggered. The middle ware will as usual send pass the request to the controller.&lt;/li&gt;
&lt;li&gt;The controller performs the operations and returns the response to the middleware.&lt;/li&gt;
&lt;li&gt;The middleware here will return the response to frontend. &lt;strong&gt;Thus causing no delays to the frontend.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;In background the middle ware will do the following.

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;random percentage&lt;/strong&gt; is calculated. Ex: 3&lt;/li&gt;
&lt;li&gt;This random percentage is compared with the &lt;strong&gt;release percentage&lt;/strong&gt; set either as an environment variable or any flags like LaunchDarkly. &lt;strong&gt;Hence if release percentage is set to 5 then there only 5% of chance that the process continues.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If the above condition matches, then call the microservice API.&lt;/li&gt;
&lt;li&gt;Compare and log the API processing time and response.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can gradually increase the release percentage whenever there are no issues with monolith and microservice API responses. Ex: start with 5 then increase to 25, then to 50, then to 75 and finally to 100.&lt;/p&gt;

&lt;p&gt;Using this setup without even affecting existing monolith API you can test the new microservice API. This will be most effective on APIs that are very sensitive, like billing or most used, like getting main job list.&lt;/p&gt;

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

&lt;p&gt;Once the test succeeds and at 100% you are not receiving any issues. Then you can disable this experiment &amp;amp; monolith APIs and directly enable the microservice APIs.&lt;/p&gt;

</description>
      <category>microservices</category>
      <category>webdev</category>
      <category>testing</category>
    </item>
    <item>
      <title>Code review checklist</title>
      <dc:creator>Vishesh</dc:creator>
      <pubDate>Wed, 01 Feb 2023 05:20:24 +0000</pubDate>
      <link>https://dev.to/vishesh/code-review-checklist-6in</link>
      <guid>https://dev.to/vishesh/code-review-checklist-6in</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%2Fuploads%2Farticles%2Flq4mbsuj3ro1vaq4jzij.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flq4mbsuj3ro1vaq4jzij.png" alt="Image description" width="311" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code style
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Code duplication&lt;/li&gt;
&lt;li&gt;Naming of methods&lt;/li&gt;
&lt;li&gt;Naming of variables&lt;/li&gt;
&lt;li&gt;Use of Constants for unmodified variables&lt;/li&gt;
&lt;li&gt;Developer checklist&lt;/li&gt;
&lt;li&gt;Scope and size of the PR (Can it be split?)&lt;/li&gt;
&lt;li&gt;Is it tackling too many features/bugs at once?&lt;/li&gt;
&lt;li&gt;Commit messages: conventional commits&lt;/li&gt;
&lt;li&gt;No commented code checked in&lt;/li&gt;
&lt;li&gt;Async/Awaits handled correctly&lt;/li&gt;
&lt;li&gt;Any video or screenshot to support the bug fix or feature?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Documentation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;API documentation for new/updated endpoints&lt;/li&gt;
&lt;li&gt;PR description&lt;/li&gt;
&lt;li&gt;Ticket reverence (Ex: Jira ticket)&lt;/li&gt;
&lt;li&gt;Updated README.md&lt;/li&gt;
&lt;li&gt;Typos/Grammar&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tests
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Positive cases&lt;/li&gt;
&lt;li&gt;Negative cases&lt;/li&gt;
&lt;li&gt;Error/Exception handling&lt;/li&gt;
&lt;li&gt;Tests naming&lt;/li&gt;
&lt;li&gt;Unit tests&lt;/li&gt;
&lt;li&gt;Service tests if necessary&lt;/li&gt;
&lt;li&gt;Mocking/stubbing&lt;/li&gt;
&lt;li&gt;Increase coverage&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Logical implementation&lt;/li&gt;
&lt;li&gt;Is the code release complaint (Feature flags or environment variables are up to date?)&lt;/li&gt;
&lt;li&gt;No hardcoded values (Date or time)&lt;/li&gt;
&lt;li&gt;Time complexity&lt;/li&gt;
&lt;li&gt;Timezone aware&lt;/li&gt;
&lt;li&gt;Error handling&lt;/li&gt;
&lt;li&gt;Null references&lt;/li&gt;
&lt;li&gt;Default values&lt;/li&gt;
&lt;li&gt;Single responsibility (SOLID principles)&lt;/li&gt;
&lt;li&gt;Open for extension/Closed for modification&lt;/li&gt;
&lt;li&gt;Interface segregation&lt;/li&gt;
&lt;li&gt;Dependency injection (Avoid circular / unused dependencies)&lt;/li&gt;
&lt;li&gt;Dependencies&lt;/li&gt;
&lt;li&gt;Updated package.json =&amp;gt; updated lock files (yarn or npm)&lt;/li&gt;
&lt;li&gt;Verify any newly added packages (Is it up to date and licensed)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Bugfixes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Is the bug fixed in all impacted modules?&lt;/li&gt;
&lt;li&gt;Does it require to communicate to customers?&lt;/li&gt;
&lt;li&gt;Does it impact other teams? Do we need to inform them?&lt;/li&gt;
&lt;li&gt;How well is the bug documented&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Secrets should be in configuration and values should never be in the code.&lt;/li&gt;
&lt;li&gt;No self-made encryption&lt;/li&gt;
&lt;li&gt;No client-side encryption&lt;/li&gt;
&lt;li&gt;SQL Injection&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Authorization&lt;/li&gt;
&lt;li&gt;Sensitive data in response payloads&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ops
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Logging where needed, with appropriate levels&lt;/li&gt;
&lt;li&gt;Environment variables added across all environments&lt;/li&gt;
&lt;li&gt;New packages should not fail the build&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Database
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Normalization&lt;/li&gt;
&lt;li&gt;Indexes for queries&lt;/li&gt;
&lt;li&gt;Indexes for new fields&lt;/li&gt;
&lt;li&gt;N+1 queries&lt;/li&gt;
&lt;li&gt;Backward compatibility&lt;/li&gt;
&lt;li&gt;Migration&lt;/li&gt;
&lt;li&gt;Rollback strategy (if needed)&lt;/li&gt;
&lt;li&gt;Data types&lt;/li&gt;
&lt;li&gt;Cascade deletes&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;RESTful endpoints routes&lt;/li&gt;
&lt;li&gt;HTTP semantics in response codes&lt;/li&gt;
&lt;li&gt;Uniform response format in the service&lt;/li&gt;
&lt;li&gt;Is backward compatibility managed?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frontend
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Is the output screenshot or video available to verify the changes&lt;/li&gt;
&lt;li&gt;Code / Design should work across all supported devices (Mobile, tablets, etc..)&lt;/li&gt;
&lt;li&gt;APIs should follow the API documentation&lt;/li&gt;
&lt;li&gt;DRY. Is the same code duplicated more than twice?&lt;/li&gt;
&lt;li&gt;Are functions/classes/components reasonably small (not too big)?&lt;/li&gt;
&lt;li&gt;Code has no any linter errors or warnings&lt;/li&gt;
&lt;li&gt;No console.logs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Additional resources:
&lt;/h2&gt;

&lt;p&gt;The Code Review Pyramid - &lt;a href="https://www.morling.dev/blog/the-code-review-pyramid/" rel="noopener noreferrer"&gt;https://www.morling.dev/blog/the-code-review-pyramid/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>web3</category>
      <category>blockchain</category>
      <category>offers</category>
    </item>
    <item>
      <title>My experience as a professional Developer</title>
      <dc:creator>Vishesh</dc:creator>
      <pubDate>Sat, 10 Dec 2022 13:07:44 +0000</pubDate>
      <link>https://dev.to/vishesh/developer-role-expectations-2i3g</link>
      <guid>https://dev.to/vishesh/developer-role-expectations-2i3g</guid>
      <description>&lt;p&gt;I am also learning !! This is just a note from my observations. Excited to hear form you... do comment.&lt;/p&gt;

&lt;p&gt;The meaning around being an efficient developer or analyst or any other entry or median level tech role always changes based on the organisation / team you work in. But based on working with local and offshore teams for last 5yrs. I understood few basic expectations that teams have when you want to work with them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;General:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Have an &lt;strong&gt;open mind&lt;/strong&gt;. Do not be judgemental. This will help you to think in all angles.&lt;/li&gt;
&lt;li&gt;Do &lt;strong&gt;not multi-task&lt;/strong&gt;. It will never end good.&lt;/li&gt;
&lt;li&gt;Learn to &lt;strong&gt;say no&lt;/strong&gt; humbly. Else no one will know your limits, even you. Can end up taking more work or responsibility than you can handle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share&lt;/strong&gt; learnings and short comings as much as possible. Both organisation wise and team wise. This will promote team culture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ownership&lt;/strong&gt; - you must be able to understand how your code / solution works. The full flow from program / server start to end.&lt;/li&gt;
&lt;li&gt;Ability to &lt;strong&gt;learn&lt;/strong&gt; - There is nothing wrong in not knowing anything, after all Software field is widening in all verticals every day. Difficult to always stay on top. But should be eager to know and learn about it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Communication&lt;/strong&gt; - Ability to communicate the solution to all levels of the organisation (Team, Leads, Managers and Management).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Coding:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Follow and enforce the latest &lt;strong&gt;standards&lt;/strong&gt; in code.&lt;/li&gt;
&lt;li&gt;Follow up and stay in sync with the &lt;strong&gt;latest&lt;/strong&gt; changes and upgrades in the programming language you are working.&lt;/li&gt;
&lt;li&gt;Always analyse the &lt;strong&gt;impacts&lt;/strong&gt; before coding. Understand the whole picture both feature wise and code wise.

&lt;ul&gt;
&lt;li&gt;Can create sequence diagrams.&lt;/li&gt;
&lt;li&gt;Note down knowns and unknowns in an excel and explore.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Always add &lt;strong&gt;test&lt;/strong&gt; cases and mockups. Cannot stress enough about this. Because by adding just unit test's you can easily notice the gaps and come up with some innovative ideas to improve existing code.&lt;/li&gt;
&lt;li&gt;Always watch out for more improvements and have a plan to tackle them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prioritise&lt;/strong&gt; - Split the work efficiently and reorganise based on priority.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask for help&lt;/strong&gt; or co-work as much as possible (Don't hesitate)&lt;/li&gt;
&lt;li&gt;Every piece of code you write should be &lt;strong&gt;production ready&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Apart from this if you are &lt;strong&gt;senior&lt;/strong&gt;. Then there is added expectations like below,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Team culture&lt;/strong&gt; - Share all discussion with the team. Be transparent as much as possible.&lt;/li&gt;
&lt;li&gt;Involve the whole team in any decision making. It will increase the team morale.&lt;/li&gt;
&lt;li&gt;Delegate work - &lt;strong&gt;Plan and share&lt;/strong&gt; work as much as possible with team. Try to ensure everyone in the team get to work in all parts of the program as much as possible so it's both exciting and relaxing for all.&lt;/li&gt;
&lt;li&gt;Be &lt;strong&gt;available&lt;/strong&gt; to all in the team. It will help others in the team to move without waiting.&lt;/li&gt;
&lt;li&gt;Consistently &lt;strong&gt;review&lt;/strong&gt; work and give feedback actively.&lt;/li&gt;
&lt;li&gt;Receive &lt;strong&gt;feedbacks&lt;/strong&gt; regularly and adapt accordingly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High level thinking&lt;/strong&gt; - Having an eye on next upcoming work and organising them appropriately. So the team can take it up without any major hiccups.&lt;/li&gt;
&lt;li&gt;Actively participate in &lt;strong&gt;product level discussions&lt;/strong&gt; and mention improvements or concerns.&lt;/li&gt;
&lt;li&gt;Ability to quickly switch from high level work like building CI/CD, new microservice architectures, etc to coding a feature and raising PRs.&lt;/li&gt;
&lt;li&gt;Need to learn &lt;strong&gt;high level tech concepts and strategies&lt;/strong&gt;. Actively suggest the same with the team and stakeholders.
Ex: Using deployment automation using terraform and Github workflows.&lt;/li&gt;
&lt;li&gt;Actively &lt;strong&gt;collaborate&lt;/strong&gt; with other seniors or teams in the organisation. Everyone can share and learn.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Phew !! 😰 It might me overwhelming at first but biggest learning for me was to keep clam and enjoy the process. No need to rush. Everyone have their own unique way to handing things.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>cpp</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Technical Design Document - Microservice</title>
      <dc:creator>Vishesh</dc:creator>
      <pubDate>Sat, 24 Sep 2022 11:02:28 +0000</pubDate>
      <link>https://dev.to/vishesh/technical-design-document-microservice-200g</link>
      <guid>https://dev.to/vishesh/technical-design-document-microservice-200g</guid>
      <description>&lt;p&gt;A Technical Design Document (TDD) is written by the development team and describes the minute detail of the entire design of the system that the team is going to build. It's is a &lt;code&gt;design blueprint&lt;/code&gt; for an application or feature.&lt;/p&gt;

&lt;p&gt;It should include both functional and technical design. The functional design specifies how a program will behave to outside agents and the technical design describes how that functionality is to be implemented in code.&lt;/p&gt;

&lt;p&gt;The primary function of a TDD is to,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🗣️ Communicate the technical details of the work to be done to members of the team and clients.&lt;/li&gt;
&lt;li&gt;💡 Gain clear understanding of the system to built. Which will help in planing the development.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The process of writing the TDD forces to organize thoughts and consider every aspect of the design, ensuring that we haven’t left anything out.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do i know? 📜
&lt;/h2&gt;

&lt;p&gt;I recently got hands on a monolithic system where the stakeholders gave us a problem that there is too much dependency. They had almost 5-6 team working on a single code base and it was almost impossible for the teams to sync with a single release cycle. Hence too much of unfinished code started moving to production, causing many issues.&lt;/p&gt;

&lt;p&gt;Thus we were tasked to create a microservice for one of the module. Luckily, thanks to by team lead, I got the opportunity to do the work. Hence I got to create a TDD. Got involved in many discussions and refined the TDD. It was an eye opener, despite having some experience previously in similar work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing a TDD 📖
&lt;/h2&gt;

&lt;p&gt;Writing TDD is exciting because its about building a new system / feature. But TDD is not something that can be written overnight. It is a long progress often with many review cycles. It must go through BA (Business Analyst), project lead, project manager and stakeholders. Hence should be written clear and crisp. It must be well detailed explaining the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Product requirements&lt;/strong&gt; - Here the feature overview and justification is written. This section must answer two important questions. What is the problem identified? and what are the goals?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical requirements&lt;/strong&gt; - The product requirements are translated into technical requirements — what the system needs to accomplish? More like goals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical design&lt;/strong&gt; - This contains a technical design of the solution to the requirements outlined in the previous steps. This should explain what will be the system architecture? What will be the system inputs? and what should be the expected outputs? Lot of flow diagrams will help to understand the design quicker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementation&lt;/strong&gt; - This explains how we are planing to execute the design. Generally split into phases, where each phase delivers executable and working part of the output. Final phase will deliver the complete product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing&lt;/strong&gt; - This should explain how testing is planned. Manual or automation? Should we include load testing? Why so?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Release process&lt;/strong&gt; - This should outline the plan with respect to releases. At which dates each phase and the final product will be available to the customers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Bounded context&lt;/strong&gt; - Bounded Context is a central pattern in Domain-Driven Design. The general rule is that a service should do "one thing". For example, if we set &lt;code&gt;sales&lt;/code&gt; as the boundary context then this service must only be responsible for sales data and features. Other features like support should not be accessed directly or updated in this service. However, only when absolutely necessary, sales service can communicate with support service for any data exchange. More about bounded context here (&lt;a href="https://www.martinfowler.com/bliki/BoundedContext.html)%5Bhttps://www.martinfowler.com/bliki/BoundedContext.html%5D"&gt;https://www.martinfowler.com/bliki/BoundedContext.html)[https://www.martinfowler.com/bliki/BoundedContext.html]&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TDD Template
&lt;/h2&gt;

&lt;h3&gt;
  
  
  First page
&lt;/h3&gt;

&lt;p&gt;Name TDD&lt;br&gt;
Status: Draft / In Progress / In Review / Approved&lt;/p&gt;

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

&lt;h4&gt;
  
  
  Problem Identified
&lt;/h4&gt;

&lt;p&gt;Describe about the problem in detail. Explain the impacts and issues that the problem is causing.&lt;/p&gt;

&lt;h4&gt;
  
  
  Goals identified
&lt;/h4&gt;

&lt;p&gt;List the goals to achieve for solving the problem. Describe about each goal in short sentences.&lt;/p&gt;

&lt;h4&gt;
  
  
  Supporting documents
&lt;/h4&gt;

&lt;p&gt;List of supporting documents that can be additional reading.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical overview
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Existing system architecture
&lt;/h4&gt;

&lt;p&gt;Describe about existing system architecture. This could include an architecture diagram and description.&lt;/p&gt;

&lt;h4&gt;
  
  
  Execution plan
&lt;/h4&gt;

&lt;p&gt;Describe about proposed system architecture. This could include an architecture diagram and description.&lt;/p&gt;

&lt;h4&gt;
  
  
  Data Model
&lt;/h4&gt;

&lt;p&gt;Describe how the data is stored. This could include a description of the database schema.&lt;/p&gt;

&lt;h4&gt;
  
  
  Interface/API Definitions
&lt;/h4&gt;

&lt;p&gt;Describe how the various components talk to each other. For example, if there are REST endpoints, describe the endpoint URL and the format of the data and parameters used.&lt;/p&gt;

&lt;h4&gt;
  
  
  CI/CD
&lt;/h4&gt;

&lt;p&gt;Describe on how to automate continuous code integration and continuous deployment. Explain the strategy and tools to be used.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fault tolerence
&lt;/h4&gt;

&lt;p&gt;Describe the fault tolerance strategy. How will the system react to various known / unknown issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Open Questions and Assumptions
&lt;/h3&gt;

&lt;p&gt;Add any unanswered questions or assumptions here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Abandoned Ideas
&lt;/h3&gt;

&lt;p&gt;Describe any ideas that were discussed but not considered for the design or solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional observations / Tips 💁‍♂️
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Draw as much as you can. 1 picture can explain 1000 words. No matter how hard you try, it's difficult to explain to multiple readers. Sometimes if it's too long, it can get frustrating to read. Hence create flow diagrams, architecture diagrams, bullet points, etc... You can use &lt;a href="https://app.diagrams.net/"&gt;draw.io&lt;/a&gt; or &lt;a href="https://whimsical.com/"&gt;Whimsical&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;60% of TDD I built was of diagrams. This actually helped in understanding. One of the reviewers actually confirmed that there were lesser doubts especially because of the diagrams. It helped in understanding the design.&lt;/li&gt;
&lt;li&gt;Often we can easily explain things in single line just like in emails. But that never works out in TDD because the readers here are both experienced engineers and also business stakeholders. We will receive many questions. Thus we need to be clear on every claim or fact that we add in the document. For example, if we are to say that the API performance will improve. Though it might be obvious, we are supposed to tell how? and by how much it improves? Perhaps even do a POC to get the metrics. But we must have the metrics else it's hard to prove the statements.&lt;/li&gt;
&lt;li&gt;Some popular questions - Why? Why should we do this? Why exactly like this? Why not use different method?&lt;/li&gt;
&lt;li&gt;Always remember to check for the best possible ways. If there are multiple ways, do mention them and also confirm which to pick.&lt;/li&gt;
&lt;li&gt;Always plan in incremental release model. (i.e) Create the product in small working parts. Hence every part is testable and can be released to production in quick time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Finally
&lt;/h2&gt;

&lt;p&gt;Hope it was helpful. Let me know your experience of building exciting systems in the comments !! Have a good day !!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>architecture</category>
      <category>computerscience</category>
      <category>microservices</category>
    </item>
    <item>
      <title>JavaScript responsive snake game</title>
      <dc:creator>Vishesh</dc:creator>
      <pubDate>Sun, 25 Jul 2021 05:47:56 +0000</pubDate>
      <link>https://dev.to/vishesh/javascript-responsive-snake-game-478n</link>
      <guid>https://dev.to/vishesh/javascript-responsive-snake-game-478n</guid>
      <description>&lt;p&gt;My first JS game - &lt;a href="https://lnkd.in/gDG9Vhc"&gt;https://lnkd.in/gDG9Vhc&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have always been grinding to do the best side project.Most usefull one for interviews as well. But it takes toll after sometime. Hence, i worked and created something simple and fun.&lt;/p&gt;

&lt;p&gt;This is a snake game. I tried to make it as much fun as possible. Please play and let me know. Some of its &lt;strong&gt;highlights&lt;/strong&gt; are,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Its completely responsive. Work in mobile and web.&lt;/li&gt;
&lt;li&gt;For mobile mode. i have developed a joystick which can be used for snake navigation. Its custom built and not copied or referenced from anywhere.&lt;/li&gt;
&lt;li&gt;It has sound effect. Just for fun.&lt;/li&gt;
&lt;li&gt;Its has humorous descriptions and funny messages.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Challenges
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Responsive design&lt;/strong&gt; - It was very challenging. I had to adjust canvas based on the size. On top of that i had to adjust the snake size as well. This completely alters calculations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Joystick&lt;/strong&gt; - Tried various other ways but joystick was difficult to design as it had to work in all mobile devices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Random food&lt;/strong&gt; - Generating a random number between the canvas sizes was also a challenge. But was not very difficult.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>html</category>
      <category>css</category>
    </item>
    <item>
      <title>Has anyone checked how C pointers work when it is compiled(Emscripten) to WASM file in WebAssembly ?</title>
      <dc:creator>Vishesh</dc:creator>
      <pubDate>Mon, 09 Nov 2020 11:19:00 +0000</pubDate>
      <link>https://dev.to/vishesh/has-anyone-checked-how-c-pointers-work-when-it-is-compiled-emscripten-to-wasm-file-in-webassembly-5h2e</link>
      <guid>https://dev.to/vishesh/has-anyone-checked-how-c-pointers-work-when-it-is-compiled-emscripten-to-wasm-file-in-webassembly-5h2e</guid>
      <description>&lt;p&gt;I know this is more of a question. But there is no much discussion or clarity on this. OR atleast i was unable to find. I have a question for the same in stackoverflow with absolutely no answers or even comments &lt;a href="https://stackoverflow.com/questions/64447082/how-to-use-c-compiled-wasm-functions-in-js-pointer-logic-do-not-work-as-expect"&gt;https://stackoverflow.com/questions/64447082/how-to-use-c-compiled-wasm-functions-in-js-pointer-logic-do-not-work-as-expect&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webassembly</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>emscripten</category>
    </item>
  </channel>
</rss>
