<?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: JingIsCoding</title>
    <description>The latest articles on DEV Community by JingIsCoding (@jingiscoding).</description>
    <link>https://dev.to/jingiscoding</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%2F719835%2F6f592bcd-fe53-4c97-be4a-bc201daf63bd.jpeg</url>
      <title>DEV Community: JingIsCoding</title>
      <link>https://dev.to/jingiscoding</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jingiscoding"/>
    <language>en</language>
    <item>
      <title>Efficiently Manage Distributed Locks with Redis: A Go-Based Solution</title>
      <dc:creator>JingIsCoding</dc:creator>
      <pubDate>Sun, 20 Oct 2024 20:14:00 +0000</pubDate>
      <link>https://dev.to/jingiscoding/efficiently-manage-distributed-locks-with-redis-a-go-based-solution-4h01</link>
      <guid>https://dev.to/jingiscoding/efficiently-manage-distributed-locks-with-redis-a-go-based-solution-4h01</guid>
      <description>&lt;p&gt;Distributed locks are essential in systems where multiple processes compete for shared resources. Whether it’s database access or file modifications, preventing race conditions is crucial. In this article, I will propose a &lt;a href="https://github.com/JingIsCoding/redis-lock" rel="noopener noreferrer"&gt;Redis-based distributed locking implementation&lt;/a&gt; in Go, which can be used to synchronize tasks across multiple servers.&lt;/p&gt;

&lt;p&gt;The main challenge in distributed locking is ensuring that locks are released in case of failure, avoiding deadlocks, and managing contention. Our Redis lock library, built in Go, solves these issues by ensuring that locks are automatically released and queued up requests are managed efficiently.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/JingIsCoding/redis-lock" rel="noopener noreferrer"&gt;This library&lt;/a&gt; is built with several features designed to make distributed locking simple and reliable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automatic Lock Expiration&lt;/strong&gt;: Locks are automatically released after a timeout if they are not explicitly unlocked, preventing deadlocks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queueing Mechanism&lt;/strong&gt;: Contending requests are queued, ensuring that they are granted access in a first-come, first-served manner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event Subscription&lt;/strong&gt;: The library leverages Redis’s Pub/Sub mechanism to listen for key expiration or deletion events, ensuring lock handover is efficient.
Now, Let’s start by diving into the components and understand how they work from a high level:&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Role of LockManager
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;LockManager&lt;/code&gt; plays a key role in managing the lifecycle of locks, handling the communication with Redis, and coordinating the locking requests. It’s responsible for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Acquiring locks&lt;/strong&gt;: It handles requests to acquire locks on specific keys in Redis, ensuring that only one process or thread can hold a lock on a given key at any time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queueing lock requests&lt;/strong&gt;: If a lock is already held by another process, the LockManager adds the lock request to a queue and waits for Redis notifications indicating that the lock has been released or expired.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Releasing locks&lt;/strong&gt;: It ensures that locks are released correctly by verifying that the process attempting to release the lock is the one that holds it (based on the unique lock value).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Listening to keyspace events&lt;/strong&gt;: The manager subscribes to Redis keyspace events, such as key expiration or deletion, to know when locks are released and to notify waiting processes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managing multiple locks&lt;/strong&gt;: The LockManager can handle multiple lock requests simultaneously, making it suitable for distributed systems with many concurrent processes.
The Lock function of LockManager takes a context and a key, attempts to acquire the lock, queues requests that cannot immediately obtain the lock, and it will return a Lock struct that we we will talk about in later sections.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;manager&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;lockManager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Lock&lt;/span&gt; &lt;span class="p"&gt;{&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 &lt;code&gt;Lock&lt;/code&gt; function is designed to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate a unique value for each lock attempt.&lt;/li&gt;
&lt;li&gt;Ensure that only the process/thread that acquires the lock can release it.&lt;/li&gt;
&lt;li&gt;Use Redis’s atomic operations to safely acquire the lock.&lt;/li&gt;
&lt;li&gt;Queue lock requests and listen for key expiration events via Redis Pub/Sub.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Lock object
&lt;/h2&gt;

&lt;p&gt;Now, once you obtain the Lock object, you have access to Unlock and Wait functions are designed to work within the object. These functions are critical for managing the lifecycle of a lock and handling the result of acquiring it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unlock Function&lt;/strong&gt;&lt;br&gt;
The Unlock function is responsible for releasing the lock when the thread or process is done with the resource. It ensures that only the thread or process that owns the lock (i.e., the one that holds the correct lock value) can release it. Let's break down how this works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;releaseLock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&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;&lt;strong&gt;Wait Function&lt;/strong&gt;&lt;br&gt;
The Wait function allows the caller to wait until the result of attempting to acquire the lock is available. This is particularly useful in cases where lock contention occurs, and a process is queued, waiting for the lock to become available.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resultChan&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;:&lt;br&gt;
&lt;strong&gt;Channel-based Waiting&lt;/strong&gt;: The Lock object has a resultChan channel, which is used to communicate the result of the lock acquisition attempt. When the lock is acquired (or failed), the result is sent through this channel. The Wait function simply blocks until a result is available.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Non-blocking Execution&lt;/strong&gt;: When a process attempts to acquire a lock using Lock(), it doesn’t need to block the entire thread while waiting. Instead, it can call Wait(), which will block only until a result is ready. The resultChan allows for asynchronous communication between the locking logic and the calling code, making the design non-blocking.&lt;/p&gt;

&lt;p&gt;Result Object: The function returns a result object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Ok&lt;/span&gt;    &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In summary, the key features of this library is its ability to handle high concurrency while ensuring that locks are released in a timely manner. By using Redis’s TTL feature, locks are automatically released if the process holding the lock fails.&lt;/p&gt;

&lt;p&gt;Redis-based distributed locks are a powerful solution for managing shared resources in distributed systems. This Go library makes it easy to implement robust locking mechanisms that are scalable, efficient, and fault-tolerant. Check out the repository here and start building reliable distributed systems today!&lt;/p&gt;

&lt;p&gt;Interested in contributing or have questions? Feel free to open issues or pull requests on the &lt;a href="https://github.com/JingIsCoding/redis-lock" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>redis</category>
      <category>go</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>What a number</title>
      <dc:creator>JingIsCoding</dc:creator>
      <pubDate>Wed, 20 Dec 2023 17:32:44 +0000</pubDate>
      <link>https://dev.to/jingiscoding/what-a-number-om2</link>
      <guid>https://dev.to/jingiscoding/what-a-number-om2</guid>
      <description>&lt;p&gt;Here at &lt;a href="//www.chowbus.com"&gt;chowbus.com&lt;/a&gt;, we are building Point of Sales solution for restaurant, one of the most essential part of the application logic to accurately calculate subtotal, tax, discounts, fees and totals for an order, While this might seem like a straightforward task initially, it entails some nuances.&lt;/p&gt;

&lt;p&gt;As a standard practice, we employ big integers to represent monetary amounts. This helps us avoid floating-point precision errors that can occur during addition, subtraction, and multiplication between integers. Concretely, $100.00 is represented as the integer 10000 in our system.&lt;/p&gt;

&lt;p&gt;Discount in our user case comes with either fixed amount or percentage form, and they can be applied on either order or dishes, Adding complexity, we must distribute the order discount proportionally among each dish when customers opt to pay separately for different items. The allocation of the discount respects the subtotal of each dish.&lt;/p&gt;

&lt;p&gt;Consider the scenario where there is an order discount of $1.00 with two dishes priced at $4 and $6. In this case, dish 1 would carry a discount of $0.4, and dish 2 would carry a discount of $0.6. Handling cases where the discount is not evenly divisible by the dish subtotals requires us to distribute the remainders as evenly as possible. For instance, if the discount is $1 with three dishes priced at $3 each, each dish would carry a discount of $0.33, with a remainder of $0.01. This remainder is allocated to one of the dishes.&lt;/p&gt;

&lt;p&gt;To demonstrate the point, let’s look at some code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;DiscountType&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="n"&gt;FixedAmount&lt;/span&gt; &lt;span class="n"&gt;DiscountType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"fixed_amount"&lt;/span&gt;
 &lt;span class="n"&gt;Percentage&lt;/span&gt;  &lt;span class="n"&gt;DiscountType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"percentage"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Discount&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;// other fields&lt;/span&gt;
  &lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt; &lt;span class="c"&gt;// precision to 4 decimal places, divide by 10000 when used as precentage&lt;/span&gt;
  &lt;span class="n"&gt;DiscountType&lt;/span&gt; &lt;span class="n"&gt;DiscountType&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;discount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Discount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subtotal&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int64&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;discount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DiscountType&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;FixedAmount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c"&gt;// no easy way to get max or min, unless convert to float64 and use math&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;discount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;subtotal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;subtotal&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;discount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&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="c"&gt;// have to switch between number types&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subtotal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;discount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="m"&gt;10000&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 code to allocate order discount to dishes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;allocatedDiscount&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dish&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dishes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;weight&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dish&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubTotal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Subtotal&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="n"&gt;discount&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderDiscount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="n"&gt;dishDeductible&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;dishItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubTotal&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;dishItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PreTaxLineItemsDiscount&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;dishItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PreTaxOrderDiscount&lt;/span&gt;
  &lt;span class="c"&gt;// make sure each dish can not be deducted to negative subtotal&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;discount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dishDeductible&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;discount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dishDeductible&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;discount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;allocatedDiscount&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;discount&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dishes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PreTaxOrderDiscount&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;discount&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// allocate the remainder&lt;/span&gt;
&lt;span class="n"&gt;remainder&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;orderDiscount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;allocatedDiscount&lt;/span&gt;
&lt;span class="n"&gt;dishSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dishes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;remainder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;dishIndex&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dishSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;cart&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CartData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DishItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dishIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PreTaxOrderDiscount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubTotal&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PreTaxLineItemsDiscount&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PreTaxOrderDiscount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;cart&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CartData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DishItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dishIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PreTaxOrderDiscount&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, there are lots of type casting between numbers, and what is more unpredictable in this code is that, if the order subtotal is 0, then weight is +/-Inf, and we learnt it the hard way.&lt;/p&gt;

&lt;p&gt;Admittedly, it is true that here is ample room for code refactoring to enhance cleanliness, However, the points I am trying make is that we have to deal with this kind of logic through out the calculation logic, which makes the code tedious to write and hard to read for following reasons.&lt;/p&gt;

&lt;p&gt;Have to constantly type casting between number types.&lt;br&gt;
Need to shift the decimal digit for example from 10 to 0.01or vice versa.&lt;br&gt;
Need a clear way to round up or down.&lt;br&gt;
Need to make sure division is safely handle regardless the number types.&lt;br&gt;
That is why we open source this library &lt;a href="https://github.com/JingIsCoding/number"&gt;https://github.com/JingIsCoding/number&lt;/a&gt; from our code base to mitigate some of the issues. so we can make the code looks more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;DiscountType&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="n"&gt;FixedAmount&lt;/span&gt; &lt;span class="n"&gt;DiscountType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"fixed_amount"&lt;/span&gt;
 &lt;span class="n"&gt;Percentage&lt;/span&gt;  &lt;span class="n"&gt;DiscountType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"percentage"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Discount&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;// other fields&lt;/span&gt;
  &lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Number&lt;/span&gt;
  &lt;span class="n"&gt;DiscountType&lt;/span&gt; &lt;span class="n"&gt;DiscountType&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;discount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Discount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subtotal&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;discount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DiscountType&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;FixedAmount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;discount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subtotal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;discount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ShiftDecimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subtotal&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;allocatedDiscount&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Number&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dish&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dishes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dish&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubTotal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Divide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Subtotal&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="c"&gt;// deal with divide by 0&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;discount&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderDiscount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RoundDown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;dishDeductible&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;dishItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubTotal&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;dishItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PreTaxLineItemsDiscount&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;dishItem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PreTaxOrderDiscount&lt;/span&gt;
  &lt;span class="c"&gt;// make sure each dish can not be deducted to negative subtotal&lt;/span&gt;
  &lt;span class="n"&gt;discount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;discount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dishDeductible&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;discount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsGreaterThan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;allocatedDiscount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;allocatedDiscount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dishes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PreTaxOrderDiscount&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;discount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetInt&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;Hopefully you would find this library useful, and leave feedback to the repo if you like.&lt;/p&gt;

</description>
      <category>go</category>
      <category>number</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
    <item>
      <title>How we build and maintain a Golang API service</title>
      <dc:creator>JingIsCoding</dc:creator>
      <pubDate>Wed, 27 Oct 2021 19:29:39 +0000</pubDate>
      <link>https://dev.to/jingiscoding/how-we-build-and-maintain-a-golang-api-service-5h01</link>
      <guid>https://dev.to/jingiscoding/how-we-build-and-maintain-a-golang-api-service-5h01</guid>
      <description>&lt;p&gt;Building a scalable, secure, high available api service is not an easy task, especially if there is rapid development and deployment been constantly made. It requires clean and tests-covered codebase, robust infrastructure, real-time visibility and monitoring into system behavior and performance, error reporting and incident triggering system, and more importantly, people need to foster an engineering culture that aims at digging out the root cause instead of putting out patches to fix the immediate problem, beware of the tech debt and understand the tradeoff of short-term deliverables versus long term maintainability.&lt;/p&gt;

&lt;p&gt;Here at &lt;a href="https://www.chowbus.com"&gt;chowbus&lt;/a&gt;, we have been building microservices on Golang/Ruby for a few years now, we are still learning and experimenting in various areas. Meanwhile, there are also some working practices/paradigms I’d like to discuss with the community. In this article we will share a boilerplate that is a simplified version extracted from the production codebase and we will talk about some basic ideas that should help maintaining the project quality and extends its lifespan.&lt;/p&gt;

&lt;p&gt;Please refer to &lt;a href="https://github.com/JingIsCoding/api-server-boilerplate"&gt;https://github.com/JingIsCoding/api-server-boilerplate&lt;/a&gt; for a quick setup. We will not dive into the details of the boilerplate in the article as the readme file provides a much deeper introductions on different aspects of the codebase.&lt;/p&gt;

&lt;h4&gt;
  
  
  Hierarchy and data-flow
&lt;/h4&gt;

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

&lt;p&gt;One general rule when build an application is to keep modules/layers loosely coupled with each other. In the context of Golang, you might want to always declare interfaces between modules/layers, that will make tasks easier like writing unit-testings since you could easily mock out dependencies to cover all logic path.&lt;/p&gt;

&lt;p&gt;Another aspect is to be careful about the object that you pass down the call stack, think about if you pass a request object from client all the way to the data layer. This will implicitly bind all of the components together and make it hard to reuse the underlying logic if you ever need to add another endpoint.&lt;/p&gt;

&lt;p&gt;Once the application is up and running in production and clients or other services start to consume the APIs, it then comes the real challenge where we need to alter the existing behavior and provide more functionalities to meet new business needs but have to not break existing clients that might be running on different versions. In the following sections we will talk about some general ideas to keep your application healthy.&lt;/p&gt;




&lt;h4&gt;
  
  
  Clean code
&lt;/h4&gt;

&lt;p&gt;If you have not, I would strongly recommend you take the time and read this book &lt;a href="https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882"&gt;https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are many good things in this book, one particular thing can not be emphasized enough is paying attention to the naming of the variables, function and modules, and keep revisiting if you could think of something better. Just mention a few cases here.&lt;/p&gt;

&lt;h5&gt;
  
  
  Be specific as much as possible.
&lt;/h5&gt;

&lt;p&gt;instead of use variable name like item or data&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for _, item := range productService.GetProducts() {
   ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Better to just name thing as it is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for _, product := range productService.GetProducts() {
   ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;that way, you will know right away what you are dealing with and refer to it as &lt;code&gt;product&lt;/code&gt; in the block.&lt;/p&gt;

&lt;h5&gt;
  
  
  Single(Limited) responsibility
&lt;/h5&gt;

&lt;p&gt;With the growth of complexity of the business logic and expansion of functionalities, unavoidably, we will find both the length of the source file and the length of certain functions get longer and messier to a point where one file might be 1000 lines long and one function that takes 10 arguments with nested if else blocks, which later becomes untestable because the tests cases will grow exponentially with the number of arguments. To manage this better, alway try to ask the questions that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Does the function do its name implies? For example, there might be a function called &lt;code&gt;createUser&lt;/code&gt; and in the code it not only creates the user but also sends out an welcome email. If that is the case then we should move the sending email to another function and test them separately, if they have to be called in one transaction, then we could wrap them into another caller function called &lt;code&gt;createUserAndSendEmail&lt;/code&gt; so other ppl will know the side effects of the function on this abstraction level.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Should we break up one file into smaller (more specific) files? Assuming we have a &lt;code&gt;order.go&lt;/code&gt; file that deals with all order related functions, when it starts to get out of hand we could try to break it into files that concerns different aspects of this domain. For instance, we could create a &lt;code&gt;order_creator.go&lt;/code&gt; that specifically deals with order creation procedures, and a &lt;code&gt;order_history.go&lt;/code&gt; that keeps the history for user to query at anytime, and maybe an &lt;code&gt;order_report.go&lt;/code&gt; file that is for statistics purposes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  Protect the data models
&lt;/h4&gt;

&lt;p&gt;Models are at the core of your application, and remember you will not be able to change your data model easily because you always have to carry the historical data for compatibility and traceability reasons, it will take a longer and longer to migrate the old data as the application accumulates more of them. and it is also risky to edit/remove data if some part of the code still consumes them that you may not be aware of , so be extremely careful if you ever need to make changes to the core data structures.&lt;/p&gt;

&lt;h5&gt;
  
  
  Think before adding another field.
&lt;/h5&gt;

&lt;p&gt;Sometime it feels much easy to simply add another field on the data model when a new requirement comes in, for instance, if we want to know if a certain model is modified since last time it is saved, it is easy to add a boolean flag on the model to reflect that, but the potential problem is that after 3 months, people might no longer remember what was this field used for and it becomes a tech debt when refactoring. Instead, if you could derive the state from the existing &lt;code&gt;updated_at&lt;/code&gt; field before save to database to know if this data is modified.&lt;/p&gt;

&lt;h5&gt;
  
  
  Keeps your core business models from others models
&lt;/h5&gt;

&lt;p&gt;Making a distinguish between the core data models from so-called data transfer models (DTOs) that is used to transfer data between layers and modules is a good practice, since the DTOs will likely change for different purposes with the refactoring of function argument and return types. However, the core data models should remain the same as long as the business domains that we are dealing with remain the same.&lt;/p&gt;

&lt;h5&gt;
  
  
  Maintain a consistent yet flexible interface
&lt;/h5&gt;

&lt;p&gt;Another very challenge task most of the APIs has to overcome is to support multiple versions of the clients at the same time, an iOS application might take up to days or even weeks to pass the apple review and release a new version, the adoption rate will also slowly goes up unless you kill the older version completely. So in the most cases, we have to support older api schemas and delivery new functions with new schemas at the same time. the approach we took is to alway version our APIs like &lt;code&gt;/api/v1/user&lt;/code&gt; &lt;code&gt;/api/v2/user&lt;/code&gt; and define request and response objects separately (some ppl define serializers which is also fine) so we could easily adjust the client schema without affecting how the application works internally.&lt;/p&gt;




&lt;h4&gt;
  
  
  Testing
&lt;/h4&gt;

&lt;p&gt;At current stage, we have full coverage on unit tests and integration (API) tests with a few UI tests as we still rely on QA to do regressions on every release. If you have limited engineering resource like us that build and maintain full coverage E2E tests is not feasible, I would recommend to focus on the quality of the unit tests as writing unit test is not only about the correctness of the program but more importantly it serves as “document” that each test case explains a situation that a function should cover, this is important because as the application get larger and more complicated, there will be more edge cases that will be easily forgotten and neglected, which is often the obstacle when refactoring the code because ppl fear any change could break existing behavior.&lt;/p&gt;

&lt;h5&gt;
  
  
  Constantly refactoring
&lt;/h5&gt;

&lt;p&gt;No matter how careful we build the application, it will always get messier and harder to manage, not only because the accumulation of business logics but also the team is learning and discovering new way to solve certain problems, it is always necessary to spend time to revisit the existing code.&lt;/p&gt;

&lt;p&gt;One thing we find to be quite fun is that we always allocate a few hours of the Friday afternoon to allow engineers to demo their PRs or proposals in a live session to all the engineers and explain the rational behind it, that way it encourages ppl to communicate and everyone would have chance to either learn from others or give some input.&lt;/p&gt;




&lt;h4&gt;
  
  
  Conclusion.
&lt;/h4&gt;

&lt;p&gt;Building a solid software requires a engineering culture rather than a few practices, we should always pay attention to small things and keep rethinking our approach and make changes even if it does not have directly or immediately benefit to the product.&lt;/p&gt;

</description>
      <category>go</category>
      <category>microservices</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
