<?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: Peter Andrew</title>
    <description>The latest articles on DEV Community by Peter Andrew (@peterchu999).</description>
    <link>https://dev.to/peterchu999</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%2F748563%2F7e19a353-c64f-4c39-b2ae-47a148286b39.png</url>
      <title>DEV Community: Peter Andrew</title>
      <link>https://dev.to/peterchu999</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/peterchu999"/>
    <language>en</language>
    <item>
      <title>Sorting Algorithm Theory VS Implementation 🧐 ?</title>
      <dc:creator>Peter Andrew</dc:creator>
      <pubDate>Sun, 07 Jan 2024 14:44:19 +0000</pubDate>
      <link>https://dev.to/peterchu999/sorting-algorithm-theory-vs-implementation--40ga</link>
      <guid>https://dev.to/peterchu999/sorting-algorithm-theory-vs-implementation--40ga</guid>
      <description>&lt;h2&gt;
  
  
  Why Sorting?
&lt;/h2&gt;

&lt;p&gt;I started &lt;strong&gt;learning Golang&lt;/strong&gt; last week and to solidify my understanding of the syntax, I practiced using Leetcode. This led me to revisit the algorithms I learned in my university years, which I had long forgotten. The merge sort algorithm, in particular, sparked my interest and motivated me to learn more about sorting.&lt;/p&gt;

&lt;p&gt;Here are the references for the sorting algorithm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bubble Sort&lt;/strong&gt;, which had the time complexity of (n^2)

&lt;ul&gt;
&lt;li&gt;more on bubble sort &lt;a href="https://www.geeksforgeeks.org/bubble-sort/"&gt;https://www.geeksforgeeks.org/bubble-sort/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merge Sort&lt;/strong&gt;, which had the time complexity of (n * log(n))

&lt;ul&gt;
&lt;li&gt;more on merge sort &lt;a href="https://www.geeksforgeeks.org/merge-sort/"&gt;https://www.geeksforgeeks.org/merge-sort/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quick Sort&lt;/strong&gt;, which had the time complexity of (n * log(n))

&lt;ul&gt;
&lt;li&gt;more on quick sort &lt;a href="https://www.geeksforgeeks.org/quick-sort/"&gt;https://www.geeksforgeeks.org/quick-sort/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;Since &lt;strong&gt;people rarely use bubble sort&lt;/strong&gt; due to its time complexity and perhaps because they prefer to use the built-in sort function, &lt;strong&gt;I wonder how inefficient it actually is&lt;/strong&gt; . Thus to find out, I conducted a simple experiment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Experiment
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;experiment&lt;/strong&gt; aims to &lt;strong&gt;track the time&lt;/strong&gt; needed for the &lt;code&gt;merge&lt;/code&gt;, &lt;code&gt;bubble&lt;/code&gt;, &lt;code&gt;quick&lt;/code&gt;, and &lt;code&gt;built-in&lt;/code&gt; (&lt;code&gt;sort.Ints&lt;/code&gt;) sorting function &lt;strong&gt;to sort the array&lt;/strong&gt;. The whole experiment setup can be seen on my GitHub repo&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/peterchu999"&gt;
        peterchu999
      &lt;/a&gt; / &lt;a href="https://github.com/peterchu999/sorting-experiment"&gt;
        sorting-experiment
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="MD"&gt;
&lt;h1&gt;
Comparison Between Sorting Algorithm (with Go)&lt;/h1&gt;
&lt;h2&gt;
My Post&lt;/h2&gt;
&lt;p&gt;Checkout my post on dev.to: &lt;a href="https://dev.to/peterchu999/sorting-algorithm-theory-vs-implementation--40ga/" rel="nofollow"&gt;https://dev.to/peterchu999/sorting-algorithm-theory-vs-implementation--40ga/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
Thought&lt;/h2&gt;
&lt;p&gt;By theory &lt;code&gt;bubble sort&lt;/code&gt; would had the &lt;code&gt;n^2&lt;/code&gt; complexity, and the rest &lt;code&gt;(merge,quick,built-in sort func)&lt;/code&gt; would had &lt;code&gt;n*log(n)&lt;/code&gt; complexity. here is the graph example :&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/peterchu999/sorting-experiment./visualization/theory-comparison-sort-300.png"&gt;&lt;img alt="Theory Sorting Visulization" src="https://res.cloudinary.com/practicaldev/image/fetch/s--DBRixEr9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/peterchu999/sorting-experiment./visualization/theory-comparison-sort-300.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;but when I implemented all of the sorting algorithm in golang, here is the graph for &lt;strong&gt;300&lt;/strong&gt; array length test:&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/peterchu999/sorting-experiment./visualization/comparison-between-sort-300.png"&gt;&lt;img alt="Sorting Algorithm Visualization" src="https://res.cloudinary.com/practicaldev/image/fetch/s--r171UZyz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/peterchu999/sorting-experiment./visualization/comparison-between-sort-300.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/peterchu999/sorting-experiment./visualization/combined-comparison-between-sort-300.png"&gt;&lt;img alt="Combined Sorting Algorithm Vis" src="https://res.cloudinary.com/practicaldev/image/fetch/s--96jzXfuh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/peterchu999/sorting-experiment./visualization/combined-comparison-between-sort-300.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let see closely on the first &lt;strong&gt;50&lt;/strong&gt; random array sorting benchmark. We could see up to ~25 array size, bubble sort perform better than &lt;code&gt;merge&lt;/code&gt; and &lt;code&gt;built-in&lt;/code&gt; sort. However, based on the theory it aren't suppose to be. Here os the graph comparison&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/peterchu999/sorting-experiment./visualization/theory-comparison-sort-50.png"&gt;&lt;img alt="Theory Sorting Visulization 50 Size array" src="https://res.cloudinary.com/practicaldev/image/fetch/s--jHSiHxbB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/peterchu999/sorting-experiment./visualization/theory-comparison-sort-50.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/peterchu999/sorting-experiment./visualization/combined-comparison-between-sort-50.png"&gt;&lt;img alt="Combined Sorting Algorithm Vis with 50 array" src="https://res.cloudinary.com/practicaldev/image/fetch/s--37JTl13V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/peterchu999/sorting-experiment./visualization/combined-comparison-between-sort-50.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Wierd isn't it ? but when we see the bigger picture the theory would align with the implementation. Let see the on &lt;strong&gt;1000&lt;/strong&gt; array size&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/peterchu999/sorting-experiment./visualization/theory-comparison-sort-1000.png"&gt;&lt;img alt="Theory Sorting Visulization 1000 Size array" src="https://res.cloudinary.com/practicaldev/image/fetch/s--BveU6U23--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/peterchu999/sorting-experiment./visualization/theory-comparison-sort-1000.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/peterchu999/sorting-experiment./visualization/combined-comparison-between-sort-1000.png"&gt;&lt;img alt="Combined Sorting Algorithm Vis with 1000 array" src="https://res.cloudinary.com/practicaldev/image/fetch/s--dQskxzEd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/peterchu999/sorting-experiment./visualization/combined-comparison-between-sort-1000.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/peterchu999/sorting-experiment./visualization/comparison-between-sort-1000.png"&gt;&lt;img alt="Sorting Algorithm Visualization" src="https://res.cloudinary.com/practicaldev/image/fetch/s--iK0ln69w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/peterchu999/sorting-experiment./visualization/comparison-between-sort-1000.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
Running Workflow&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;./run-all.sh [n]&lt;/code&gt;, change &lt;code&gt;[n]&lt;/code&gt; with the number of max array size for benchmark&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Running Python Visualization&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pip install -r&lt;/code&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/peterchu999/sorting-experiment"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;This is how the time tracking 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="n"&gt;TestSorting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sfc&lt;/span&gt; &lt;span class="n"&gt;SortFunction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&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="n"&gt;scopedTestList&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&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;arr&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
    &lt;span class="nb"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scopedTestList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// copy the unsorted array to avoid side effects on other test&lt;/span&gt;
    &lt;span class="n"&gt;startTime&lt;/span&gt; &lt;span class="o"&gt;:=&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;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// track start time&lt;/span&gt;
    &lt;span class="n"&gt;sfc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scopedTestList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// call sorting function&lt;/span&gt;
    &lt;span class="n"&gt;endTime&lt;/span&gt; &lt;span class="o"&gt;:=&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;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// track end time&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;endTimeMerge&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;startTimeMerge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Nanoseconds&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// test return startTime - endTime&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here is the test setup:&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;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;1&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;arrLen&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="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;testCase&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;GenerateUniqueRandomNumbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// generate i length of random numbers array&lt;/span&gt;
    &lt;span class="n"&gt;mergeT&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestSorting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MergeSortVoid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;testCase&lt;/span&gt;&lt;span class="p"&gt;)&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;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&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;Millisecond&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// incase computation degrade machine and make later case worst&lt;/span&gt;
    &lt;span class="n"&gt;bubbleT&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestSorting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BubbleSortVoid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;testCase&lt;/span&gt;&lt;span class="p"&gt;)&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;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&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;Millisecond&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// incase computation degrade machine and make later case worst&lt;/span&gt;
    &lt;span class="n"&gt;quickT&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestSorting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;QuickSortVoid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;testCase&lt;/span&gt;&lt;span class="p"&gt;)&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;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&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;Millisecond&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// incase computation degrade machine and make later case worst&lt;/span&gt;
    &lt;span class="n"&gt;biT&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt;  &lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestSorting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ints&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;testCase&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;ul&gt;
&lt;li&gt;loop until &lt;code&gt;arrLen&lt;/code&gt; which was the maximum number of the array length.&lt;/li&gt;
&lt;li&gt;for each iteration, generate a random number array with the length of the current iteration index (i)&lt;/li&gt;
&lt;li&gt;for each iteration, tracked the time needed for each sorting function to sort the array&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;the gathered data from the experiment above were saved and visualized by using Python code. The &lt;strong&gt;visualization plot the time taken&lt;/strong&gt; for &lt;strong&gt;each array length&lt;/strong&gt; to be &lt;strong&gt;sorted&lt;/strong&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;Interesting results are shown when using a small array length. &lt;strong&gt;Let's examine the visualized graph comparing the complexity of theoretical sorting algorithms with the actual implementation time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o1WjrKcN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lx6und2z4bf31q11kpc5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o1WjrKcN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lx6und2z4bf31q11kpc5.png" alt="theory visualization of 50 array length" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6ZcI6BSv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/14cx4ptsa74oerr2kwr0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6ZcI6BSv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/14cx4ptsa74oerr2kwr0.png" alt="sort experiment visualization of 50 array length" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Up to&lt;/strong&gt; an &lt;strong&gt;array length&lt;/strong&gt; of around &lt;strong&gt;30&lt;/strong&gt;, &lt;strong&gt;bubble sort&lt;/strong&gt;, which was &lt;strong&gt;presumably&lt;/strong&gt; deemed &lt;strong&gt;slower&lt;/strong&gt; according to theory, was &lt;strong&gt;actually faster&lt;/strong&gt; than the &lt;code&gt;merge&lt;/code&gt; and &lt;code&gt;built-in&lt;/code&gt; sort functions. It's quite surprising 🤯 !&lt;/p&gt;

&lt;p&gt;However, when we &lt;strong&gt;look at the bigger picture&lt;/strong&gt;, the &lt;strong&gt;theory and implementation align&lt;/strong&gt; . Here is the visualization of the theoretical sort complexity versus the sort implementation time for an array length of 1000.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XqO1SAB7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rnsc3zsz8p65ase580xz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XqO1SAB7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rnsc3zsz8p65ase580xz.png" alt="theory visualization of 1000 array length" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YKOyQVXq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t7nc0320zctjb4sgpl1m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YKOyQVXq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t7nc0320zctjb4sgpl1m.png" alt="sort experiment visualization of 1000 array length" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;In the end, &lt;strong&gt;when the array size is small&lt;/strong&gt;, the sorting operation &lt;strong&gt;takes a very short amount of time&lt;/strong&gt; (just a few nanoseconds), so &lt;strong&gt;no one really notices the difference&lt;/strong&gt;. &lt;strong&gt;What truly matters is that the &lt;code&gt;n * log(n)&lt;/code&gt; sort algorithm&lt;/strong&gt; (merge sort, quick sort) we learned &lt;strong&gt;performs significantly better on larger array size&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Please check out my &lt;a href="https://github.com/peterchu999/sorting-experiment"&gt;code&lt;/a&gt; and feel free to experiment with it. &lt;strong&gt;If you find any flaws or errors, please create an issue on GitHub or leave a comment 💬 below!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm still &lt;strong&gt;unsure why bubble sort performs better on small datasets&lt;/strong&gt;. &lt;strong&gt;My guess&lt;/strong&gt; is that &lt;strong&gt;the operation to create a new array&lt;/strong&gt; in merge sort &lt;strong&gt;takes longer than&lt;/strong&gt; simply &lt;strong&gt;swapping values&lt;/strong&gt; between indices. What do you think 🤔 ? &lt;strong&gt;Please share your thoughts and comments 🙌!&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Acknowledgment
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://unsplash.com/photos/long-exposure-photography-of-road-and-cars-NqOInJ-ttqM"&gt;https://unsplash.com/photos/long-exposure-photography-of-road-and-cars-NqOInJ-ttqM&lt;/a&gt; (cover image)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://geeksforgeeks.org/"&gt;https://geeksforgeeks.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://blog.boot.dev/golang/quick-sort-golang/"&gt;https://blog.boot.dev/golang/quick-sort-golang/&lt;/a&gt; (quick sort)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.tutorialspoint.com/how-to-generate-a-array-of-unique-random-numbers-in-golang"&gt;https://www.tutorialspoint.com/how-to-generate-a-array-of-unique-random-numbers-in-golang&lt;/a&gt; (generate random number array)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>algorithms</category>
      <category>programming</category>
      <category>discuss</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Creating Robust Front-End Validation using react-hooks-form 👑</title>
      <dc:creator>Peter Andrew</dc:creator>
      <pubDate>Thu, 30 Mar 2023 08:50:44 +0000</pubDate>
      <link>https://dev.to/peterchu999/creating-robust-front-end-validation-in-react-hooks-form-2ome</link>
      <guid>https://dev.to/peterchu999/creating-robust-front-end-validation-in-react-hooks-form-2ome</guid>
      <description>&lt;h2&gt;
  
  
  Why Frontend validation ?
&lt;/h2&gt;

&lt;p&gt;Sometimes I wonder why we bother to make validation on the frontend side when there is a bunch of it on the backend side 🤔. Reflecting on managing several frontend projects here is the key takeaway :&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Saving Time and Resources
&lt;/h3&gt;

&lt;p&gt;By doing front end validation we could reduce the time taken by network request. Moreover, we are also saving back end resources as we reduce the number of invalid requests from the front end.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Better User Experience
&lt;/h3&gt;

&lt;p&gt;Frontend validation could improve the user experience by providing instant feedback when they make an error. This way user could correct their errors in real-time which increases their engagement with our application.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Increase security and data consistency
&lt;/h3&gt;

&lt;p&gt;Backend validation wasn’t bullet proof, sometimes human error happen and we forgot some validation and our data become inconsistent (such as unusual email, or phone number format). Therefore, having the 2nd layer protection was definitely better than only one layer. Moreover, front end validation decrease security issue to some extends (such as SQL injection, XSS, etc). &lt;/p&gt;

&lt;h2&gt;
  
  
  Why use react-hooks-form ?
&lt;/h2&gt;

&lt;p&gt;The reason was quite clear from the official website &lt;em&gt;“Performant, flexible and extensible forms with easy-to-use validation. - &lt;a href="https://react-hook-form.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;react-hook-form.com&lt;/strong&gt;&lt;/a&gt;”&lt;/em&gt; 🤩&lt;em&gt;.&lt;/em&gt; personally, I used react hooks form because of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Isolate re-render:&lt;/strong&gt; optimizing react rendering could be overwhelming for beginner - intermediate developers. React hooks form helps to mitigate those issue in the form, it makes sure every field only render when they are being edited.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent Validation Strategy:&lt;/strong&gt; react hooks form provides a structured way of doing validation. Hence, every developer would use the same validation strategy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer Experience:&lt;/strong&gt; this library provides intuitive API that could be used seamlessly across React and React Native environments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Built-in validation in react-hooks-form
&lt;/h2&gt;

&lt;p&gt;Let's dive into the code. Assume that we want to create a form for pre-ordering sandwiches. Here's the basic code: &lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/peterchu999" rel="noopener noreferrer"&gt;
        peterchu999
      &lt;/a&gt; / &lt;a href="https://github.com/peterchu999/hooks-validation-article" rel="noopener noreferrer"&gt;
        hooks-validation-article
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;hooks-validation-article&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a href="https://stackblitz.com/edit/react-vewtyv" rel="nofollow noopener noreferrer"&gt;Edit on StackBlitz ⚡️&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/peterchu999/hooks-validation-article" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;The code above is basic and not yet integrated with &lt;code&gt;react-hook-form&lt;/code&gt;. To integrate, we need to install &lt;code&gt;react-hook-form&lt;/code&gt; by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;yarn&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;hook&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to call the &lt;code&gt;useForm&lt;/code&gt; hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;formState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useForm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;useForm&lt;/code&gt; returns several functions, but for our demonstration, we will focus on the following essential ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;register&lt;/code&gt;: this function binds our input data to &lt;code&gt;useForm&lt;/code&gt;. We need to specify the input &lt;code&gt;name&lt;/code&gt; in the &lt;code&gt;register&lt;/code&gt; function, and optionally, we can pass validation rules.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;handleSubmit&lt;/code&gt;: this function binds our form's submit behavior, it takes 2 functions as parameter:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;onSuccess&lt;/code&gt;: the callback function that receives all data from registered fields. This function is triggered only when the data passes validation.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onError&lt;/code&gt;: the callback function that captures validation errors for all registered fields. This function is triggered only when validation fails.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;formState.errors&lt;/code&gt;: this state contains the error messages from our fields' validation.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Now, register our fields with &lt;code&gt;register&lt;/code&gt; function then bind our form &lt;code&gt;onSubmit&lt;/code&gt; with &lt;code&gt;handleSubmit:&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;successfully submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&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="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;some validation had failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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="si"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
...
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Name&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
...
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Email&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
...
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Phone Number&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;phone-number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
...
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Sandwich Qty&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;qty&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far, we have integrated the react-hook-form into our project 😎. For the final step in this section, let's set up field validation and display an error message when a field fails validation.&lt;/p&gt;

&lt;p&gt;to add &lt;code&gt;rules&lt;/code&gt; (validation) to the fields we could pass the &lt;code&gt;RegisterConfiguration&lt;/code&gt; object as the 2nd parameter to the &lt;code&gt;register&lt;/code&gt; function :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;50%&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;flexDirection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;column&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;justifyContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;space-between&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Name&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="p"&gt;{&lt;/span&gt; 
              &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name is required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
              &lt;span class="na"&gt;minLength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
              &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name should have minimum 3 characther&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; 
            &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; 
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;}&lt;/span&gt; 
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above indicates that the &lt;code&gt;name&lt;/code&gt; field is &lt;strong&gt;required&lt;/strong&gt; and &lt;strong&gt;must have a minimum of 3 characters&lt;/strong&gt;. If the validation fails, an error message will appear stating that either &lt;code&gt;name is required&lt;/code&gt; or &lt;code&gt;name should have a minimum of 3 characters&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;the validation rules quite vary, here is the list:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;maxLength&lt;/code&gt;: validate the value of the field to have a maximum character length of x&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;minLength&lt;/code&gt;: validate the value of the field to have a minimum character length of x&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max&lt;/code&gt;: validate the fields with number type to had max value of x&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max&lt;/code&gt;: validate the fields with number type to had min value of x&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pattern&lt;/code&gt;: validate the value of the field to match the Regex pattern&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;validate&lt;/code&gt;: validate the value of the field to pass the custom-made function&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the demonstration of validating &lt;code&gt;email&lt;/code&gt; fields with custom made function to make sure it contains &lt;code&gt;@dev.to&lt;/code&gt; value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email fields are required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@dev.to&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                             &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;need to use dev to email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
                    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&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="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the latest code that implements build-in validation of react-hook-form could be found on &lt;code&gt;build-in-validate&lt;/code&gt; branch&lt;/p&gt;

&lt;h2&gt;
  
  
  Robust validation in react-hooks-form
&lt;/h2&gt;

&lt;p&gt;This method of creating validation using react-hook-form is concise and helps us organize our validation more efficiently 👀. However, there is a more robust approach to validation in react-hook-form that involves using &lt;code&gt;resolvers&lt;/code&gt; (validation resolvers). &lt;code&gt;Resolvers&lt;/code&gt; is an adapter that integrates react-hook-form with 3rd-party validation libraries such as &lt;code&gt;class-validator&lt;/code&gt;, &lt;code&gt;joi&lt;/code&gt;, &lt;code&gt;zod&lt;/code&gt;, etc. By delegating the validation to these libraries, we can have a more robust validation in our apps 🔥.&lt;/p&gt;

&lt;p&gt;Suppose we choose &lt;code&gt;class-validator&lt;/code&gt; as our validation library. To install &lt;code&gt;class-validator&lt;/code&gt; and &lt;code&gt;resolver&lt;/code&gt;, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add @hookform/resolvers class-transformer class-validator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, create a &lt;code&gt;class-validator&lt;/code&gt; schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export class SandwichPO {
  @MinLength(5)
  name: string;

  @IsEmail()
  email: string;

  @IsPhoneNumber()
  phoneNumber: string;

  @IsNumber()
  qty: string;
}

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

&lt;/div&gt;



&lt;p&gt;Using this schema, we can create resolvers and bind them to our &lt;code&gt;useForm&lt;/code&gt; hooks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const resolver = classValidatorResolver(SandwichPO);

...
const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({ resolver: resolver });
...

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

&lt;/div&gt;



&lt;p&gt;That's it! Everything is now aligned 🥳. Delegating our validation to a 3rd-party library makes our code more organized and robust 🤟.&lt;/p&gt;

&lt;p&gt;latest code from resolver implementation could be found on &lt;code&gt;resolver-implementation&lt;/code&gt; branch&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing validation library
&lt;/h2&gt;

&lt;p&gt;When it comes to choosing a library, there are several factors that need to be considered. The rule of thumb that I follow is:&lt;/p&gt;

&lt;h3&gt;
  
  
  Experience
&lt;/h3&gt;

&lt;p&gt;When selecting a validation library, it is best to choose one that you have used before. This will reduce the learning curve and make implementation easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compatibility with the backend
&lt;/h3&gt;

&lt;p&gt;Building backend services with JavaScript has become quite common nowadays. It would be ideal to use the same validation library on both the front-end and back end to ensure consistent validation using the same schema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Library size
&lt;/h3&gt;

&lt;p&gt;Keep in mind that the size of the validation library can increase the size of your app and cause longer download times for the user. Therefore, choose a library size that is appropriate for your needs.&lt;/p&gt;

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

&lt;p&gt;Front-end validation could improve user experience, increase security, save resources, and maintain data consistency. Therefore, it’s crucial to have a robust validation mechanism. react-hook-form could help us to standardize our validation strategy and it could even help us to integrate a 3rd-party validation library using the react-hook-form resolvers.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Photo by Antonio Borriello: &lt;a href="https://www.pexels.com/photo/photo-of-macbook-air-on-a-table-next-to-house-plant-and-picture-frame-1297611/" rel="noopener noreferrer"&gt;https://www.pexels.com/photo/photo-of-macbook-air-on-a-table-next-to-house-plant-and-picture-frame-1297611/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Notion AI: &lt;a href="https://www.notion.so/product/ai" rel="noopener noreferrer"&gt;https://www.notion.so/product/ai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://react-hook-form.com/" rel="noopener noreferrer"&gt;https://react-hook-form.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/react-hook-form/resolvers" rel="noopener noreferrer"&gt;https://github.com/react-hook-form/resolvers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/peterchu999/hooks-validation-article" rel="noopener noreferrer"&gt;https://github.com/peterchu999/hooks-validation-article&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Developer And Documentation</title>
      <dc:creator>Peter Andrew</dc:creator>
      <pubDate>Sat, 01 Jan 2022 16:06:52 +0000</pubDate>
      <link>https://dev.to/peterchu999/developer-and-documentation-2pjp</link>
      <guid>https://dev.to/peterchu999/developer-and-documentation-2pjp</guid>
      <description>&lt;p&gt;Hi there, Who doesn't like to write documentation as a developer 🖐. As a developer, code, tools and my application performance is the what matters to me, I always think documentation is only necessary at the large company as a part of obligation. &lt;/p&gt;

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

&lt;p&gt;Abandoning documentation is common for beginner developer for a various different reason, there is something in common when I discuss it with my peers. It happen because we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Work on assignment project that will be dispose once our scoring done&lt;/li&gt;
&lt;li&gt;Work on personal project which the sole contributor are us&lt;/li&gt;
&lt;li&gt;Think our memory is good enough to remember everything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's not only happen because of developer, sometimes company need to dispose all "unnecessary" task to shorten business delivery time, and think it can be work on after the business is "settled" (Which for most cases it will not happen 🙄).&lt;/p&gt;

&lt;p&gt;While it's true we can still work on documentation at later time, but as a time go， it become unpaid developer debt.&lt;/p&gt;

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

&lt;p&gt;Here is my story that make me work on documentation even though I still don't like it 🤐.&lt;/p&gt;

&lt;p&gt;I've been working on certain project for around 6 month, and recently we got a new developer, and i'm in charge to onboard them. At first, I can answer the basic questions like "What language did we use?", "What's our development stack" or "How to clone and run our project locally ?"&lt;/p&gt;

&lt;p&gt;The problem arise when the new joiner ask me something specific "How is the data flow from A service to B service?" and "How we handle it from FE side, if specific case happen? ". Keep in mind that i'm not the only developer in this project and we have 0 documentation to be refer to. So I need to gather the whole team and we spent at least 1 week to just make sure every new joiner (and the old pals) understood our whole project data flow and architecture 😮‍💨.&lt;/p&gt;

&lt;p&gt;Doesn't it better if we have documented everything and when there is someone new joining, we could just send them the document ? at worst we only need to sacrifice 1 person time to help the onboarding, rather than the whole team 😱.&lt;/p&gt;

&lt;p&gt;So here is why documentation is important to me:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To centralize the knowledge of whole team&lt;/strong&gt;&lt;br&gt;
When i'm the only one working on feature A, it most likely the others teammate doesn't know the implementation detail. If in the near future they need to improve or refactor this A feature they need to ask me, to prevent this to happen i could just made a documentation that everyone can read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To retain the project knowledge even if someone leaving&lt;/strong&gt;&lt;br&gt;
People came and go, but the knowledge must retain. Ever heard "don't touch the legacy code" ? yep, it happen because the one made it have left the organization. if there is a good documentation anyone can tweak or do improvement on the legacy code (or not legacy at all since we understood them 😏)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature improvement or bug fixing&lt;/strong&gt;&lt;br&gt;
As our project scale, we can't always remember on what we've done, once the bug occur we need to re-read all of &lt;em&gt;our own&lt;/em&gt; code and try to make sense on what it done 😵‍💫. It's better if it's documented, even the others can work on it.&lt;/p&gt;

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

&lt;p&gt;There is a lot of documentation from Class, Flow, or even API documentation. but what should I make all of it ? the answer is actually &lt;strong&gt;depends&lt;/strong&gt; on our situation 🤔. If you have any thought please leave it in discussion 🙏 cause it might help me and everyone who's struggling with this.&lt;/p&gt;

&lt;p&gt;From my case this is what our team decide to documented:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API documentation (using swagger, but make it as detail as possible)&lt;/li&gt;
&lt;li&gt;Architecture Documentation (How our service connected with each other, database connection and any 3rd party)&lt;/li&gt;
&lt;li&gt;Activity Diagram (for each feature)&lt;/li&gt;
&lt;li&gt;Additional Documentation (ranging from configuration needed to custom tools tutorial)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;My story might not be the best, but the point is to encourage everyone to start the habit of documenting, even a Markdown documentation or documenting certain feature is great 🙌, at least we have a knowledge that we preserve for us and the others ✨.&lt;/p&gt;

&lt;p&gt;It might seems to waste our time when we first write it, but it save time when someone need the knowledge, we could just send them over 😉.&lt;/p&gt;

</description>
      <category>writing</category>
      <category>management</category>
      <category>productivity</category>
      <category>motivation</category>
    </item>
    <item>
      <title>Tracking in React Apps</title>
      <dc:creator>Peter Andrew</dc:creator>
      <pubDate>Sat, 25 Dec 2021 11:30:38 +0000</pubDate>
      <link>https://dev.to/peterchu999/tracking-in-react-apps-584e</link>
      <guid>https://dev.to/peterchu999/tracking-in-react-apps-584e</guid>
      <description>&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The code might not be a best practice, because it's based on personal experience.&lt;/li&gt;
&lt;li&gt;Example has been simplified, so we could focus on the tracking code and tools&lt;/li&gt;
&lt;li&gt;This post will not discuss or used any 3rd party implementation of specific tracking platform (crashlytics, data dog, sentry, mixpanel, etc)&lt;/li&gt;
&lt;li&gt;The 3rdParty Mocked code might be different from real 3rdParty API&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Notice
&lt;/h2&gt;

&lt;p&gt;if you are interested in the application code more than the tracking implementation. Leave reaction to this post, I'll consider making another post to explain it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tracking
&lt;/h2&gt;

&lt;p&gt;Nowadays, tracking user experience is a must for most application, by collecting the tracked data from user we can develop, fixing or improve our feature (especially UX). &lt;/p&gt;

&lt;p&gt;Based on my experience tracking can be differ into 2 category :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;product / marketing&lt;/code&gt;: this tracking goals is to keep track and evaluate marketing approaches (FB ads, google ads, instagram link, etc), and help product team to evaluate UX&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;error&lt;/code&gt;: this tracking purpose is to notify developer about the error that occur in production before customer making any complain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's see the implementation in react code&lt;/p&gt;

&lt;h2&gt;
  
  
  Application and tracking statement
&lt;/h2&gt;

&lt;p&gt;To implements tracking we need to at least having an application. I have create a base project at:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/peterchu999"&gt;
        peterchu999
      &lt;/a&gt; / &lt;a href="https://github.com/peterchu999/react-tracking-impl"&gt;
        react-tracking-impl
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;What is the app functionality ?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a news curation app that use &lt;a href="https://newsapi.org/"&gt;newsapi&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;there is 2 tab &lt;code&gt;Home&lt;/code&gt; and &lt;code&gt;Top News&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Each tab have &lt;code&gt;refresh&lt;/code&gt; news functionality&lt;/li&gt;
&lt;li&gt;Each news card linked to respective article website&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;What are we going to track ?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;track every click on &lt;code&gt;go to source&lt;/code&gt; button, we want to evaluate whether user usually go to &lt;code&gt;tops news&lt;/code&gt; tab or not, so the Data expected looks like :
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  eventName: 'click_go_to_source',
  page: 'Home / TopNews'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;track every click on &lt;code&gt;refresh feed&lt;/code&gt; button, we want to evaluate whether user click &lt;code&gt;refresh feed&lt;/code&gt; button or not so the data expected looks like :
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  eventName: 'refresh_feed',
  page: 'Home / TopNews'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;track error when &lt;code&gt;fetching data&lt;/code&gt;, we want to track every error occur when fetching data. Data expect to looks like :
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  eventName: 'error_fetch',
  page: 'Home / TopNews',
  errorReason: stringify error object
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Usual Tracking (Direct Approach)
&lt;/h2&gt;

&lt;p&gt;Basically it's just calling 3rd party sdk / api for event tracking or logging on every click handler or error catch&lt;/p&gt;

&lt;p&gt;In this code example we will use Mocked &lt;code&gt;DataDog&lt;/code&gt; for our &lt;strong&gt;error&lt;/strong&gt; tracking and &lt;code&gt;MixPanel&lt;/code&gt; for our &lt;strong&gt;click&lt;/strong&gt; tracking. &lt;/p&gt;

&lt;p&gt;The code implementation can be seen in &lt;a href="https://github.com/peterchu999/react-tracking-impl/tree/direct"&gt;link&lt;/a&gt;. &lt;/p&gt;
&lt;h3&gt;
  
  
  Detail Code go through
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Click Go To Source Track&lt;/strong&gt;&lt;br&gt;
every time the user click &lt;code&gt;go to source&lt;/code&gt; this code will send over the data to mock &lt;code&gt;MixPanel&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ArticleCard.js
...
// line 7
const handleClick = () =&amp;gt; {
  const eventName = "click_go_to_source";
  const unique_id = uuid();
  MixPanel.track(eventName, unique_id, {
    page,
  });
  ...
};
....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Click Refresh Feed Track&lt;/strong&gt;&lt;br&gt;
every time the user click &lt;code&gt;refresh feed&lt;/code&gt; this code will send over the data to mock &lt;code&gt;MixPanel&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Home.js or TopNews.js
...
// line 26
const onRefreshClick = () =&amp;gt; {
  const eventName = "refresh_feed";
  const unique_id = uuid();
  MixPanel.track(eventName, unique_id, {
    page,
  });
  ...
};
....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Fetch News error Track&lt;/strong&gt;&lt;br&gt;
every time our fetch to news from newsapi failed, this code will send  over the &lt;code&gt;fetch_error&lt;/code&gt; data to mock &lt;code&gt;DDlog&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Home.js or TopNews.js
...
// line 15
onError: (err) =&amp;gt; {
  const eventName = "error_fetch";
  DDlog.error(eventName, {
    page,
    errorReason: JSON.stringify(err, null, 4),
  });
},
....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It seems everything to work fine 🤔, yep that's what i thought, until some changes was needed because of new feature or 3rd Party tracking platform commercial issue / fees.&lt;/p&gt;

&lt;p&gt;Imagine that we already put 100+ tracker over 10 screens, then we need to :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;change tracking platform, for example from &lt;code&gt;MixPanel&lt;/code&gt; to &lt;code&gt;Heap&lt;/code&gt;. we need to manually refactor all of our &lt;code&gt;MixPanel&lt;/code&gt; tracking code 1-by-1 😵‍💫. &lt;/li&gt;
&lt;li&gt;add additional tracking data since we have new login feature, now we want to track user data every too 🤯.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Gratefully, i encounter this problem when my tracker was still less than 20 😮‍💨. But there is a question pop up on my mind, do i need to change the code one-by-one every time there is commercial issue or new feature that affect current tracking ?&lt;/p&gt;
&lt;h2&gt;
  
  
  React Tracking
&lt;/h2&gt;

&lt;p&gt;That's what lead me to &lt;code&gt;react-tracking&lt;/code&gt; by NYT, a React specific tracking library. it helps to :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Centralize our tracking logic, yet compartmentalize tracking concerns to individual components&lt;/li&gt;
&lt;li&gt;Give tracking data a scope&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's see the code implementation &lt;a href="https://github.com/peterchu999/react-tracking-impl/tree/rtracking"&gt;link&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;We create &lt;code&gt;ReactTrackingInitializer&lt;/code&gt; HOC (High Order Component) to be our parent / root tracking wrapper.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const ReactTrackingInitializer = ({ children }) =&amp;gt; {
  const { Track } = useTracking(
    {
      // this is where the initialize data put
      trackVersion: "1.0.0",
    },
    {
      dispatch: (trackedData) =&amp;gt; {
        console.log("dispatchData", trackedData);  
    }
  );
  return &amp;lt;Track&amp;gt;{children}&amp;lt;/Track&amp;gt;;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;useTracking&lt;/code&gt; is a hooks version to implementing &lt;code&gt;react-tracking&lt;/code&gt; which suitable for functional component, find out more on their docs if you still implementing class component.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;useTracking&lt;/code&gt; takes 2 params:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;initial data, means this data available for the rest of the child component.&lt;/li&gt;
&lt;li&gt;is the options which consist of &lt;code&gt;dispatch&lt;/code&gt;,&lt;code&gt;dispatchOnMount&lt;/code&gt;,&lt;code&gt;process&lt;/code&gt;, and &lt;code&gt;fowardRef&lt;/code&gt; more detail check &lt;a href="https://github.com/nytimes/react-tracking"&gt;react-tracking&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;useTracking&lt;/code&gt; will return object with 3 properties:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;trackEvent&lt;/code&gt;: a function to send data to be process at &lt;code&gt;process&lt;/code&gt;, then &lt;code&gt;dispatch&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;getTrackingData&lt;/code&gt;: a function that return current initial data in our tracker.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Track&lt;/code&gt;: a HOC that wrapped a child component to give scope to it's initial data, &lt;code&gt;process&lt;/code&gt; and &lt;code&gt;dispatch&lt;/code&gt; logic. which later can be triggered using &lt;code&gt;trackEvent&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From the reference we can implements our 3rd Party logic at &lt;code&gt;dispatch&lt;/code&gt; option. so it will looks like this :&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
dispatch: (trackedData) =&amp;gt; {
  console.log("dispatchData", trackedData);
  const { eventName, ...restOfData } = trackedData.data;
  switch (trackedData.type) {
     case "product":
       const unique_id = uuid();
       MixPanel.track(eventName, unique_id, restOfData);
       break;
     case "error":
       DDlog.error(eventName, restOfData);
       break;
     default:
       break;
  }
},
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It looks a lot like &lt;code&gt;redux&lt;/code&gt; reducers. Now you might ask there must be a dispatch mechanism to like redux, where is it ? checkout the code at &lt;code&gt;Home.js&lt;/code&gt; line 25 - 33&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { trackEvent, Track } = useTracking({
  data: { page: "HOME" },
});

const onRefreshClick = () =&amp;gt; {
  trackEvent({ type: "product", data: { eventName: "refresh_feed" } });
  refetch();
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;the &lt;code&gt;trackEvent&lt;/code&gt; will send over the data below to our &lt;code&gt;dispatch&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ 
  type: "product", 
  data: { 
    eventName: "refresh_feed",
    page: "HOME"
  } 
  trackVersion: "1.0.0"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Wait, Where did &lt;code&gt;trackVersion: "1.0.0"&lt;/code&gt; and &lt;code&gt;page: "HOME"&lt;/code&gt; came from 🙄 ? react tracking perform a merge operation on data we sent and initial data provided. in this case :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;data we send :
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ 
  type: "product", 
  data: { 
    eventName: "refresh_feed"
  } 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;initial value on &lt;code&gt;Home.js&lt;/code&gt; useTracking :
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ 
  data: { 
    page: "HOME"
  } 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;initial value on &lt;code&gt;ReactTrackingInitializer&lt;/code&gt; useTracking:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  trackVersion: "1.0.0"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We already utilize &lt;code&gt;react-tracking&lt;/code&gt; 🎉🎉🎉, Just Note that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;there must be at least 1 component that wrapping with &lt;code&gt;&amp;lt;Track&amp;gt;&amp;lt;/Track&amp;gt;&lt;/code&gt; at root level (prefer to wrap )&lt;/li&gt;
&lt;li&gt;Initial value only available to child component if we wrapped them with &lt;code&gt;&amp;lt;Track&amp;gt;&amp;lt;/Track&amp;gt;&lt;/code&gt;. that why we wrapped &lt;code&gt;&amp;lt;ArticleCard&amp;gt;&lt;/code&gt; in &lt;code&gt;Home.js&lt;/code&gt; line 57 - 63, so it get the initial value from &lt;code&gt;Home.js&lt;/code&gt; useTracking, otherwise it will only have initial value of &lt;code&gt;ReactTrackingInitializer.js&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now back to the problem, let say we need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;change MixPanel to Heap&lt;/li&gt;
&lt;li&gt;add user data to every tracker, because we have new login feature&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;just see the difference between branch &lt;code&gt;rtracking&lt;/code&gt; and &lt;code&gt;rtracking-solution&lt;/code&gt;. &lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/peterchu999/react-tracking-impl/pull/1"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Changes need
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#1&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/peterchu999"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--CUHs5VDC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars.githubusercontent.com/u/37611509%3Fv%3D4" alt="peterchu999 avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/peterchu999"&gt;peterchu999&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/peterchu999/react-tracking-impl/pull/1"&gt;&lt;time&gt;Dec 25, 2021&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;Changes need to solve the problem statement:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;change MixPanel to Heap&lt;/li&gt;
&lt;li&gt;add user data, because we have add login feature&lt;/li&gt;
&lt;/ol&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/peterchu999/react-tracking-impl/pull/1"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;and compare it to the difference between branch &lt;code&gt;direct&lt;/code&gt; and direct-solution`. &lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/peterchu999/react-tracking-impl/pull/2"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Changes Need -&amp;gt; Direct Solution
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#2&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/peterchu999"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--CUHs5VDC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars.githubusercontent.com/u/37611509%3Fv%3D4" alt="peterchu999 avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/peterchu999"&gt;peterchu999&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/peterchu999/react-tracking-impl/pull/2"&gt;&lt;time&gt;Dec 25, 2021&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;Changes need to solve the problem statement:&lt;/p&gt;
&lt;p&gt;change MixPanel to Heap
add user data, because we have add login feature&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/peterchu999/react-tracking-impl/pull/2"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;It will more work to be done when using 3rdParty Sdk / API directly, Imagine we have 10+ MixPanel tracker, it will cost a lot of time.&lt;/p&gt;

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

&lt;p&gt;React Tracking Help us to centralize the tracking logic so if there are any changes needed we can just refactor our dispatch function.&lt;/p&gt;

&lt;p&gt;Thanks for reading, leave any comment below 😊&lt;/p&gt;

&lt;h2&gt;
  
  
  Shout Out
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/nytimes"&gt;
        nytimes
      &lt;/a&gt; / &lt;a href="https://github.com/nytimes/react-tracking"&gt;
        react-tracking
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      🎯 Declarative tracking for React apps.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;div class="ltag__link"&gt;
  &lt;a href="https://open.nytimes.com/introducing-react-tracking-declarative-tracking-for-react-apps-2c76706bb79a" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BbVvmWev--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/fit/c/96/96/1%2AmVz-V4Wqznnm7Of-DQ_skQ.jpeg" alt="Jeremy Gayed"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://open.nytimes.com/introducing-react-tracking-declarative-tracking-for-react-apps-2c76706bb79a" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Open Source: Declarative Tracking for React Apps | by Jeremy Gayed | NYT Open&lt;/h2&gt;
      &lt;h3&gt;Jeremy Gayed ・ &lt;time&gt;Oct 1, 2018&lt;/time&gt; ・ 
      &lt;div class="ltag__link__servicename"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hnDHPsJs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/medium-f709f79cf29704f9f4c2a83f950b2964e95007a3e311b77f686915c71574fef2.svg" alt="Medium Logo"&gt;
        open.nytimes.com
      &lt;/div&gt;
    &lt;/h3&gt;
&lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>javascript</category>
      <category>react</category>
      <category>monitoring</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>My First Open Source Contribution</title>
      <dc:creator>Peter Andrew</dc:creator>
      <pubDate>Mon, 06 Dec 2021 03:35:31 +0000</pubDate>
      <link>https://dev.to/peterchu999/my-first-open-source-contribution-16p6</link>
      <guid>https://dev.to/peterchu999/my-first-open-source-contribution-16p6</guid>
      <description>&lt;p&gt;It's been a while after &lt;strong&gt;Hacktoberfest 2021&lt;/strong&gt; 🤩, my very first hacktoberfest. I would like to share my story to motivate more people to start contributing to open source.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Bit about Myself
&lt;/h2&gt;

&lt;p&gt;I started to code around 2017 and my very first programming language was C (i barely remember the syntax now) for university assignment, then progressed into web development with PHP(Laravel), HTML, CSS, &amp;amp; Javascript. Since the pandemic, i had trying out various programming language and framework such as Java, Python, Swift, Typescript, React Native, Django, Nest JS, etc. Currently focus more on Javascript and Typescript as fullstack developer. &lt;/p&gt;

&lt;h2&gt;
  
  
  Open Source Encounter
&lt;/h2&gt;

&lt;p&gt;Sometimes in 2019, senior of mine introduced the concept of open source where our code publicly available, could be contribute and use by developer around the world. A lot of programming language, framework, and tools are actually open source product such as &lt;strong&gt;Linux, Firefox, Android, PHP, etc&lt;/strong&gt;. Contributing to open source can be a self satisfaction and enrich your CV as Developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  First Open Source Experience
&lt;/h2&gt;

&lt;p&gt;Based on idea above, I started to search open source project to contributed. Most of open source can be found at git repository services like &lt;strong&gt;Github, Gitlab, etc&lt;/strong&gt;. Back in the day I think that we must contribute code to the main application / library to improve, bug fix or suggest new feature (It's untrue).&lt;/p&gt;

&lt;p&gt;After a lot of searching on Github, Here was my problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I can't found any repository that suit my skill level, i try to find HTML, CSS , JS repository. The project is either too complex, undocumented or beyond my understanding.&lt;/li&gt;
&lt;li&gt;I'm not used to git version control system, I only learn about clone, pull and push.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then I gave up on the idea of contributing as a beginner. made self conclusion that only expert can contribute to the open source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hacktoberfest 2021
&lt;/h2&gt;

&lt;p&gt;A newsletter from digital ocean about hacktoberfest coming to my email. Interested by the name I read and turns out hacktoberfest is one month event where developer could share, organize and contributing to open source project. check hacktoberfest website &lt;a href="https://hacktoberfest.digitalocean.com/"&gt;https://hacktoberfest.digitalocean.com/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I sign up for the event out of eagerness what a mediocre developer like myself can do. Attend the opening meeting and got new insight of open source contribution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can contribute not only to the main code, but documentation and example code as well&lt;/li&gt;
&lt;li&gt;Most of open source project have a contribution guide&lt;/li&gt;
&lt;li&gt;You could check &lt;code&gt;Issue&lt;/code&gt; tab to know what you can contribute&lt;/li&gt;
&lt;li&gt;Some repository even need a translate contribution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What's make me more motivated is the hacktoberfest challenge. We need to made &lt;strong&gt;4 Merge Request&lt;/strong&gt; (term's for requesting our changes merge to repository) within October month, and we will &lt;strong&gt;receive t-shirts as swag&lt;/strong&gt; or they will &lt;strong&gt;have a tree planted in our name&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Long story short I completed the challenge 🎉🎉🎉. I contribute 4 MR to Appwrite repository by creating an example on how to use the Appwrite SDK. Checkout Appwrite which can make your development easier and faster. &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/appwrite"&gt;
        appwrite
      &lt;/a&gt; / &lt;a href="https://github.com/appwrite/appwrite"&gt;
        appwrite
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Secure Backend Server for Web, Mobile &amp;amp; Flutter Developers 🚀 AKA the 100% open-source Firebase alternative.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;blockquote&gt;
&lt;p&gt;It's going to get cloudy! 🌩 ☂️
The Appwrite Cloud is coming soon! You can learn more about our upcoming hosted solution and signup for free credits at: &lt;a href="https://appwrite.io/cloud" rel="nofollow"&gt;https://appwrite.io/cloud&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br&gt;
&lt;p&gt;
    &lt;a href="https://appwrite.io" rel="nofollow"&gt;&lt;img width="260" height="39" src="https://camo.githubusercontent.com/efa53eb7f28d68fc52de93df0880014b61e1d20d9b64a9cef3c84ee2626f18ad/68747470733a2f2f61707077726974652e696f2f696d616765732f61707077726974652e737667" alt="Appwrite Logo"&gt;&lt;/a&gt;
    &lt;br&gt;
    &lt;br&gt;
    &lt;b&gt;A complete backend solution for your [Flutter / Vue / Angular / React / iOS / Android / *ANY OTHER*] app&lt;/b&gt;
    &lt;br&gt;
    &lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://appwrite.io/company/careers" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/fdce0c153e22f5c48c51d7630c214df6cb71efd51be5200c3d59803c8c99e336/68747470733a2f2f696d672e736869656c64732e696f2f7374617469632f76313f6c6162656c3d5765277265266d6573736167653d486972696e6726636f6c6f723d626c7565267374796c653d666c61742d737175617265" alt="We're Hiring"&gt;&lt;/a&gt;
&lt;a href="https://hacktoberfest.appwrite.io" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/262f9d728c0c44f67808ae8cd8c94629626e472846ad4c4e76818a72c1175dcf/68747470733a2f2f696d672e736869656c64732e696f2f7374617469632f76313f6c6162656c3d6861636b746f62657266657374266d6573736167653d726561647926636f6c6f723d313931313230267374796c653d666c61742d737175617265" alt="Hacktoberfest"&gt;&lt;/a&gt;
&lt;a href="https://appwrite.io/discord?r=Github" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/f7db461c28285cf2764fb8713f5a66a46b95ea9492d2d93342b028f5df0f91e9/68747470733a2f2f696d672e736869656c64732e696f2f646973636f72642f3536343136303733303834353135313234343f6c6162656c3d646973636f7264267374796c653d666c61742d737175617265" alt="Discord"&gt;&lt;/a&gt;
&lt;a href="https://github.com/appwrite/appwrite/actions"&gt;&lt;img src="https://camo.githubusercontent.com/aa8d7b39ea90a8c0a673463ee706bf9127fb740a3e0ccfbe09a8b8a5bdb85828/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f61707077726974652f61707077726974652f74657374732e796d6c3f6272616e63683d6d6173746572266c6162656c3d7465737473267374796c653d666c61742d737175617265" alt="Build Status"&gt;&lt;/a&gt;
&lt;a href="https://twitter.com/appwrite" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/cc7fa7d5347290e1d43352135c5fa7b944fe6e511c094a29776fd9a5422807f2/68747470733a2f2f696d672e736869656c64732e696f2f747769747465722f666f6c6c6f772f61707077726974653f636f6c6f723d303061636565266c6162656c3d74776974746572267374796c653d666c61742d737175617265" alt="Twitter Account"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;English | &lt;a href="https://github.com/appwrite/appwriteREADME-CN.md"&gt;简体中文&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://appwrite.io/graphql" rel="nofollow"&gt;&lt;strong&gt;Announcing Appwrite 1.2 with GraphQL support! Learn what's new!&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Appwrite is an end-to-end backend server for Web, Mobile, Native, or Backend apps packaged as a set of Docker microservices. Appwrite abstracts the complexity and repetitiveness required to build a modern backend API from scratch and allows you to build secure apps faster.&lt;/p&gt;
&lt;p&gt;Using Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, and &lt;a href="https://appwrite.io/docs" rel="nofollow"&gt;more services&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;
    &lt;br&gt;
    &lt;a href="https://www.producthunt.com/posts/appwrite-2?utm_source=badge-top-post-badge&amp;amp;utm_medium=badge&amp;amp;utm_souce=badge-appwrite-2" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/a06ecc4da08102392730067f364bf5705ed8b0b40948b2159711ff7a596588c4/68747470733a2f2f6170692e70726f6475637468756e742e636f6d2f776964676574732f656d6265642d696d6167652f76312f746f702d706f73742d62616467652e7376673f706f73745f69643d333630333135267468656d653d6c6967687426706572696f643d6461696c79" alt="Appwrite - 100% open source alternative for Firebase | Product Hunt" width="250" height="54"&gt;&lt;/a&gt;
    &lt;br&gt;
    &lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/appwrite/appwritepublic/images/github.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EY_7ccCS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/appwrite/appwritepublic/images/github.png" alt="Appwrite"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Find out more at: &lt;a href="https://appwrite.io" rel="nofollow"&gt;https://appwrite.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Table of Contents:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;…&lt;/li&gt;&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/appwrite/appwrite"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;

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

&lt;p&gt;Here is summarize of my learning throughout my open source journey :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For a beginner we can find open source community to help and guide us, rather than searching aimlessly alone.&lt;/li&gt;
&lt;li&gt;try to find &lt;code&gt;good first issue&lt;/code&gt; label in the issue. it usually required a very basic understanding on the repository&lt;/li&gt;
&lt;li&gt;Join a contributing event like hacktoberfest to motivate ourself to make it to the end.&lt;/li&gt;
&lt;li&gt;Read the contribution guide at the repository, it gave a lot of information about the project and the rules for contributing.&lt;/li&gt;
&lt;li&gt;Some repository even have a group channel at discord or slack, join so we can communicate and learn with their team. &lt;/li&gt;
&lt;li&gt;Contributing on repository with lot of star can be a pride, because the more star the repo have it means more developer use it.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;[Hacktoberfest spesific]&lt;/em&gt; , always remember to check the &lt;code&gt;hacktoberfest&lt;/code&gt; label on the repository or issue to make our contribution counted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Contributing to open source is not an easy task, but it's not as hard as we think. we could read the hacktoberfest website which provide a video on step by step to create merge request.&lt;/p&gt;

&lt;p&gt;Thanks for reading, kindly leave a comment 😗 &lt;/p&gt;

</description>
      <category>git</category>
      <category>hacktoberfest</category>
      <category>devjournal</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>App Center Build, Distribute Android "debug" buildType issue</title>
      <dc:creator>Peter Andrew</dc:creator>
      <pubDate>Sun, 28 Nov 2021 21:28:49 +0000</pubDate>
      <link>https://dev.to/peterchu999/app-center-build-distribute-android-debug-buildtype-issue-328</link>
      <guid>https://dev.to/peterchu999/app-center-build-distribute-android-debug-buildtype-issue-328</guid>
      <description>&lt;p&gt;If you have yet familiar with App Center Build we had discussed a bit in previous article, be sure to check it out:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/peterchu999" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uxvII-V2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--pp6OWMiV--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/748563/7e19a353-c64f-4c39-b2ae-47a148286b39.png" alt="peterchu999"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/peterchu999/automate-build-with-app-center-3c7b" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Automate Build With App Center&lt;/h2&gt;
      &lt;h3&gt;Peter Andrew ・ Nov 22 '21 ・ 5 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#azure&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tutorial&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#productivity&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
 
&lt;h2&gt;
  
  
  The Issue 👀
&lt;/h2&gt;

&lt;p&gt;It's been a while since I have been using App Center build and came across couple of issue, one that really hinder my work were a known issue where we can't automatically distribute a build from android "debug" buildType. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--024knp6M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t6daof1ujjb8dperzi5v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--024knp6M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t6daof1ujjb8dperzi5v.png" alt="Can't Distribute Image" width="880" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more information read it from :&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/microsoft/appcenter/issues/844"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Option to sign and distribute debug builds to groups
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#844&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/androideveloper"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--aFNXkQtY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars.githubusercontent.com/u/1249919%3Fv%3D4" alt="androideveloper avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/androideveloper"&gt;androideveloper&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/microsoft/appcenter/issues/844"&gt;&lt;time&gt;Aug 08, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;When setting up branch configuration, I can't sign (and therefore distribute)  debug build variants. It is a common practice to send debug builds for QA testing and this limitation is strange.&lt;/p&gt;
&lt;p&gt;There a few alternatives:
-Create a separate build type (debug1) and it will work (but why the limitation exists on debug word?)
-Create a boilerplate post-build script that will distribute apks
-Create release variant and sign with debug key. Release build variant builds a little longer (proguard enabled) and that affects our workflow.&lt;/p&gt;
&lt;p&gt;Why the limitation exists? Why can't we just use debug variant as others?&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/microsoft/appcenter/issues/844"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: "debug" buildType is typically used for QA test. Because it still have code logging capability, that can help us to identify more precise causes of error. So if this are your purpose of using App Center, then the issue might become a problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Work Around 🔄
&lt;/h2&gt;

&lt;p&gt;Based on some googling and the issue above, here is the possible work around :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create a new separate buildType (ex: appcenterDebug)&lt;/li&gt;
&lt;li&gt;download the build (.apk) then re-upload it to App Center Distribute&lt;/li&gt;
&lt;li&gt;create a post-build script to automate distribution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of the options need some hassle 😅. The easiest was obviously &lt;strong&gt;download and re-upload&lt;/strong&gt; alternative, but then, it become less automation (the very first reason we use this tools).&lt;/p&gt;

&lt;p&gt;In my case, a &lt;strong&gt;separate builtType&lt;/strong&gt; make a ton of problem for managing credentials files and android permission. So it's not a suitable way out.&lt;/p&gt;

&lt;p&gt;At the time, I work on the &lt;strong&gt;create a post-build script&lt;/strong&gt; solution, since it's the best solution for my case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Distribute Automation Using Post Build Script
&lt;/h2&gt;

&lt;p&gt;For those who didn't know App Center Build Script, find out more at the &lt;a href="https://docs.microsoft.com/en-us/appcenter/build/custom/scripts/"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: it's a custom &lt;code&gt;bash&lt;/code&gt; script that run during certain build stages (post-clone, pre-build &amp;amp; post-build).&lt;/p&gt;

&lt;p&gt;In this cases, we want to utilize the &lt;code&gt;post-build&lt;/code&gt; script that run after the &lt;code&gt;.apk&lt;/code&gt; was signed &amp;amp; created.&lt;/p&gt;

&lt;h3&gt;
  
  
  Disclaimer &amp;amp; Note
&lt;/h3&gt;

&lt;p&gt;Admittedly, I am not good at bash script. The script below worked based on trial and error, if know the best practice or better version of the code, please leave a comment.&lt;/p&gt;

&lt;p&gt;If you want to try the bash script locally, install &lt;code&gt;appcenter cli&lt;/code&gt; by using &lt;code&gt;npm i appcenter-cli -g&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Bash variable that start with &lt;code&gt;ENV_&lt;/code&gt; is Something that we defined in App Center Build environment variable, to load it check out this &lt;a href="https://github.com/peterchu999/appcenter-build-test/blob/master/appcenter-pre-build.sh"&gt;&lt;code&gt;pre-build&lt;/code&gt; script&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Post Build Script
&lt;/h3&gt;

&lt;p&gt;Here is the specific script that distribute our &lt;code&gt;.apk&lt;/code&gt; to App Center Distribution.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;&lt;code&gt;APPCENTER_OUTPUT_DIRECTORY&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Variable containing the path to &lt;code&gt;.apk&lt;/code&gt; file. By default App Center have populate &lt;code&gt;APPCENTER_OUTPUT_DIRECTORY&lt;/code&gt;, we just need to concat &lt;code&gt;/[file-name].apk&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;ENV_DISTRIBUTE_MESSAGE&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Variable containing the distribution message. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w_SQ53o6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sg2d4r3wlwdnvys2ylkj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w_SQ53o6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sg2d4r3wlwdnvys2ylkj.png" alt="Distribution message" width="734" height="926"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;ENV_APPCENTER_BUILD_TOKEN&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Variable containing the App Center token. It is use to authorized App Center Cli. you could generate App Center token in App Center website over &lt;code&gt;Settings &amp;gt; App API tokens &amp;gt; New API Token&lt;/code&gt;. Make sure you choose &lt;strong&gt;Full Access&lt;/strong&gt; options when the generated API Token.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8bjZc5Tx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p26a4u215h024rz6w0bm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8bjZc5Tx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p26a4u215h024rz6w0bm.png" alt="Generate App API Token Menu" width="880" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;ENV_APP_NAME&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Variable containing App Center App Name, it is used by App Center Cli to identify which project we are working on. the easiest way is by combining your &lt;code&gt;[username]/[project name]&lt;/code&gt; in the example it was &lt;code&gt;[werdnaretep99-gmail.com/App-Center-Testing-Android]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4yuTUewr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xove5e43rpjimj2oe33s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4yuTUewr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xove5e43rpjimj2oe33s.png" alt="App Center Build URL" width="880" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;the other way, use App Center cli &lt;code&gt;appcenter apps list&lt;/code&gt; command. it will list all of the apps name we possessed.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--acAblx40--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bvzxshnsilw6imml7zwf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--acAblx40--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bvzxshnsilw6imml7zwf.png" alt="App Center Cli Apps List" width="880" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;ENV_DISTRIBUTE_GROUP&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Variable containing list of distribution group separated by comma(,), ex: 'Group 1, group 2, group n'.By default App Center already make 1 distribution group which is &lt;code&gt;Collaborators&lt;/code&gt;. You could make a new group on App Center website over &lt;code&gt;Distribute &amp;gt; Groups &amp;gt; Add New Group&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kQJJIxtL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9aj6kuovy87vqidugkkh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kQJJIxtL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9aj6kuovy87vqidugkkh.png" alt="Generated New Group Menu" width="880" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;&lt;br&gt;
Bear in mind, you might want to remove line 1 - 7, from the github gist code (because it contains credential, plus it's more dynamic to set distribution group and release note on ENV). for every variable start with &lt;code&gt;ENV_&lt;/code&gt; must be place on App Center Environment Variable or your &lt;code&gt;.env&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RIsuYdB---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/io0ke9o8f6hal0frt554.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RIsuYdB---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/io0ke9o8f6hal0frt554.png" alt="App Center Environment Variable" width="880" height="586"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Additional Post Build Script (React Native)
&lt;/h3&gt;

&lt;p&gt;If we are using react native, big chances our &lt;code&gt;Android&lt;/code&gt; and &lt;code&gt;iOS&lt;/code&gt; repository was not separated. Once we push our &lt;code&gt;appcenter-post-build.sh&lt;/code&gt; script the build will run whether we build an &lt;code&gt;Android&lt;/code&gt; project and &lt;code&gt;iOS&lt;/code&gt; project. this will inevitably become a problem for &lt;code&gt;iOS&lt;/code&gt; build. since there is no &lt;code&gt;.apk&lt;/code&gt; and the &lt;code&gt;ENV_APPCENTER_BUILD_TOKEN&lt;/code&gt; different across project.&lt;/p&gt;

&lt;p&gt;We need to make an exception, so the distribution script doesn't run when we build iOS App. My current solution is checking if default &lt;a href="https://docs.microsoft.com/en-us/appcenter/build/custom/variables/"&gt;Android Environment Variable&lt;/a&gt; available.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Trick to Set Default Distribution Group
&lt;/h3&gt;

&lt;p&gt;We might want to set default distribution group to all group. it's a bother if we need to manually update our &lt;code&gt;ENV_DISTRIBUTE_GROUP&lt;/code&gt; when we add new group. I found out a little tricks that work on my project (but i don't know if it's a best practice).&lt;/p&gt;

&lt;p&gt;App Center Cli provide a way to get list of our distribution group using &lt;code&gt;appcenter distribute groups list&lt;/code&gt; command. there is some parameters needed for the command above to work perfectly.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In the code example, we get the list of distribution group as json. We need to remapped the json into string with each elements separated by comma. I used &lt;code&gt;jq&lt;/code&gt; a json library to extract the list.&lt;/p&gt;

&lt;p&gt;After a lot of trial and error work perfectly 💪🤩.&lt;/p&gt;

&lt;p&gt;checkout the code full version on github (several branches)&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/peterchu999"&gt;
        peterchu999
      &lt;/a&gt; / &lt;a href="https://github.com/peterchu999/appcenter-build-test"&gt;
        appcenter-build-test
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
appcenter-connect&lt;/h1&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/peterchu999/appcenter-build-test"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  Personal Thought
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;post-script&lt;/code&gt; workaround might have pros and cons depending on our cases. For example we might want to use another distribution platform like &lt;code&gt;Firebase - App Distribution&lt;/code&gt;, as long as the platform accommodate cli based distribution with could use &lt;code&gt;post-script&lt;/code&gt; to distribute the build. on the contrary, if we want a simple solution (not recommended), give access and ask QA to download the build (.apk) from the App Center Build directly.&lt;/p&gt;

&lt;p&gt;Thanks for reading, constructive comment will be really appreciated ❤️ ❤️ ❤️.&lt;/p&gt;

</description>
      <category>android</category>
      <category>azure</category>
      <category>tutorial</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Automate Build With App Center</title>
      <dc:creator>Peter Andrew</dc:creator>
      <pubDate>Mon, 22 Nov 2021 09:46:30 +0000</pubDate>
      <link>https://dev.to/peterchu999/automate-build-with-app-center-3c7b</link>
      <guid>https://dev.to/peterchu999/automate-build-with-app-center-3c7b</guid>
      <description>&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;This post was full of personal preferences, and i'm writing this based on my experience as hybrid mobile (React Native) developer.&lt;/p&gt;

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

&lt;p&gt;As a mobile developer we are expected to build the apps artifacts (.apk/.ipa). Here was my problem when dealing with build/archiving apps artifacts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As the code base grew larger, so does the build time. My current project took almost 15 mins for android and 35 mins for iOS.&lt;/li&gt;
&lt;li&gt;When we are building the artifacts we can't change branch or run our code in simulator. Thus, (i assume) we can't code the new feature or fixing the issues while waiting for our build.&lt;/li&gt;
&lt;li&gt;Building artifacts take a lot of computer resources (at least for iOS).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those was my biggest problem aside from being noob to setup local environment to build the artifacts 🥲.&lt;/p&gt;

&lt;p&gt;And App Center Build is the platform to mitigate those problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's App Center Build ?
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;"The App Center Build is a service to helps you build Android, iOS, macOS, and UWP apps using a secure cloud infrastructure"&lt;/em&gt; - App Center.&lt;/p&gt;

&lt;p&gt;Basically App Center Build is a way to build our artifacts on cloud, which solve our problem 🤩.&lt;/p&gt;

&lt;p&gt;Advantages of using App Center Build (that i felt):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's easy to use, just connect it to our repo and done (for some cases configuration might be needed).&lt;/li&gt;
&lt;li&gt;It build based on branch, so no need to wait until the code merged to master.&lt;/li&gt;
&lt;li&gt;App Center came with a distribution platform as well, which integrated really well with the App Center Build.&lt;/li&gt;
&lt;li&gt;Build configuration can be cloned easily across branch.&lt;/li&gt;
&lt;li&gt;it's free (with limitation though).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to use App Center Build
&lt;/h2&gt;

&lt;p&gt;Let's use step-by-step example to demonstrate the build process:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create an App Center Account
&lt;/h3&gt;

&lt;p&gt;Here is the link to the App Center Sign Up page &lt;a href="https://appcenter.ms/create-account" rel="noopener noreferrer"&gt;click here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;[Optional]&lt;/em&gt; I have made a simple React Native Project.
&lt;/h3&gt;

&lt;p&gt;this is a template React Native screen with additional feature to take &lt;code&gt;ENV_TEXT&lt;/code&gt; key value from &lt;code&gt;.env&lt;/code&gt; and show it in the screen. &lt;a href="https://github.com/peterchu999/appcenter-build-test" rel="noopener noreferrer"&gt;link to the repo&lt;/a&gt; forked it and get along with this example.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Create App Center Project
&lt;/h3&gt;

&lt;p&gt;Create the App Center Project for both android and iOS version.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  3. Connect for repository with the App Center Build!
&lt;/h3&gt;

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

&lt;p&gt;Select the repo services, then follow the instruction. List of our repository will be shown. In this case i'll use &lt;a href="https://github.com/peterchu999/appcenter-build-test" rel="noopener noreferrer"&gt;this&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After choosing repository, the App Center will look like this: &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw50iqukl34l3fu2mj6s8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw50iqukl34l3fu2mj6s8.png" alt="app center build repo added"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Setup Build Configuration
&lt;/h3&gt;

&lt;p&gt;Actually, it's a bit impractical to say that we don't need to configure anything but if we just wanna try out it's do able.&lt;/p&gt;

&lt;p&gt;From the picture above select the master branch row, and we'll see this: &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxbjx6qpn80ps0gztmot1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxbjx6qpn80ps0gztmot1.png" alt="configure button"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;click "Configure Build" and the Configuration sidebar will appear.&lt;/p&gt;

&lt;p&gt;Actually all of the configuration name was already on point but here is some of differences when we build on local:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; &lt;code&gt;Build scripts&lt;/code&gt;: it shows what kind of build script that we have added in our repository. App Center Provide a way to run additional script command that corresponding to the build stages &lt;a href="https://aka.ms/docs/build/custom/scripts" rel="noopener noreferrer"&gt;read here for more&lt;/a&gt;. 

&lt;ul&gt;
&lt;li&gt;Note that every time we push a new build script (let says currently we just have pre-build and want to add post build), we need to resave our configuration.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;Build frequency&lt;/code&gt;: We can choose to build the apps manually by clicking "build", or build the apps from the branch every time we make a git push. Note that with automatic build, merged MR count as push and will be automatically build.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;App Center Build also provide a way to store environment variable in the build configuration.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz0ab8tedtnni5jps2mtb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz0ab8tedtnni5jps2mtb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But the value of this environment variables is a bit different than &lt;code&gt;.env&lt;/code&gt; file, it was meant to use in the build script. if we want to convert the App Center environment variable into .env, the repos &lt;code&gt;appcenter-pre-build.sh&lt;/code&gt; file already provide the code to make a &lt;code&gt;.env&lt;/code&gt; from the App Center environment variable that have start with &lt;code&gt;ENV&lt;/code&gt; key.&lt;/p&gt;

&lt;p&gt;For Signed with android, I usually do it manually in gradle. But if we want to upload &lt;code&gt;.keystore&lt;/code&gt; file and provide the value from App Center Build Configuration this &lt;a href="https://docs.microsoft.com/en-us/appcenter/build/android/code-signing" rel="noopener noreferrer"&gt;docs&lt;/a&gt; provide a great help.&lt;/p&gt;

&lt;p&gt;As for the Signed with iOS build, we need to upload &lt;code&gt;Provisioning Profile&lt;/code&gt; and &lt;code&gt;Certificate&lt;/code&gt;. We could we find it ?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provisioning Profile: Open Xcode, under `Signing &amp;amp; Capabilities Tab, there is the provisioning profile, click the "i" logo and popup will appear like the image below. &lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;from the file like icon we could drag the provisioning file&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpw2bxoscushm6rkoleg5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpw2bxoscushm6rkoleg5.gif" alt="Drag Provisioning Profile"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Certificate File: Open KeyChain Access, find Apple Development Certificate, right click and export.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8xcjm8eeq4rfq8jajzma.png" alt="Image description"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the configuration was set, click save button on the bottom right.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Build the Apps
&lt;/h3&gt;

&lt;p&gt;After we configured the build, there will be "build now" button click it and do other work's while waiting for the build done.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frq86gqx833iml3tswjfr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frq86gqx833iml3tswjfr.png" alt="Build Button"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once it's done, the "distribute" and "Download" button will appear.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyat4oysabponm0lium1g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyat4oysabponm0lium1g.png" alt="Distribute and Download"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voilà that's it ✨✨✨&lt;/p&gt;

&lt;h2&gt;
  
  
  App Center Limitation
&lt;/h2&gt;

&lt;p&gt;For the free tier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is maximal 4 hours build/month/user, means if the average of our apps build time is 20 mins, we can only build 12 times across project per month.&lt;/li&gt;
&lt;li&gt;There is maximal 30 minutes build /build, if our iOS build takes longer than 30 minutes we'll get error &lt;code&gt;##[error]The operation was canceled.&lt;/code&gt;, in this case we'll need to upgrade tier 💸.&lt;/li&gt;
&lt;li&gt;There is only 1 (machine) concurrency to process our build, need to queue the build if we try to make 2 or more simultaneous build.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Paid tier :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is maximal 60 minutes build times/build, if our iOS build takes longer than 60 minutes we'll get error &lt;code&gt;##[error]The operation was canceled.&lt;/code&gt;, for this issue currently App Center doesn't support it means we need to find alternative 🚫&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Advice
&lt;/h2&gt;

&lt;p&gt;App Center is easy to use, even at the free tier we can still use it for hobby project (just remember to turn off automatic build after push). But if we really want to upgrade to paid tier which cost $40/month/concurrent, Then make an organization on App Center and pay the bill as organization so the people under the organization can get the unlimited build times benefit (just make everybody admin).&lt;/p&gt;

&lt;p&gt;Thanks for reading, any critique, grammar correction, or comment will be appreciated 🙌&lt;/p&gt;

</description>
      <category>azure</category>
      <category>devops</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
