<?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: Victor</title>
    <description>The latest articles on DEV Community by Victor (@vponamariov).</description>
    <link>https://dev.to/vponamariov</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%2F451349%2Fa5b6b5a3-aa88-4fbf-962c-574d190026b1.jpg</url>
      <title>DEV Community: Victor</title>
      <link>https://dev.to/vponamariov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vponamariov"/>
    <language>en</language>
    <item>
      <title>18 Cards with form design tips</title>
      <dc:creator>Victor</dc:creator>
      <pubDate>Thu, 27 May 2021 12:57:34 +0000</pubDate>
      <link>https://dev.to/vponamariov/18-cards-with-form-design-tips-2dh6</link>
      <guid>https://dev.to/vponamariov/18-cards-with-form-design-tips-2dh6</guid>
      <description>&lt;p&gt;Hey devs!&lt;/p&gt;

&lt;p&gt;I made 18 cards with tips of common mistakes when designing web forms.&lt;/p&gt;

&lt;p&gt;I've also posted &lt;a href="https://twitter.com/vponamariov/status/1397891520263512066" rel="noopener noreferrer"&gt;them on Twitter&lt;/a&gt;, so if you're a Twitter user you can make a bookmark there.&lt;/p&gt;

&lt;p&gt;Here we go&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnpzwman9l4ca9nniqu8.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%2Fxnpzwman9l4ca9nniqu8.png" alt="Card number 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fniafrjf459u7dmei70p6.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%2Fniafrjf459u7dmei70p6.png" alt="Card number 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbwenkty8y3w09uu35ghb.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%2Fbwenkty8y3w09uu35ghb.png" alt="Card number 3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9sn2uvw4v9kh2o0rk4po.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%2F9sn2uvw4v9kh2o0rk4po.png" alt="Card number 4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ta680enr234788v7k8y.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%2F6ta680enr234788v7k8y.png" alt="Card number 5"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpgas2qyvwkug7vgsri0f.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%2Fpgas2qyvwkug7vgsri0f.png" alt="Card number 6"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3fe5zpvul0c0542l2u41.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%2F3fe5zpvul0c0542l2u41.png" alt="Card number 7"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsk791259e99bcmncce5e.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%2Fsk791259e99bcmncce5e.png" alt="Card number 8"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzmopswtabydob8b38mt1.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%2Fzmopswtabydob8b38mt1.png" alt="Card number 9"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcphf1b1vz80iov4ivx3v.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%2Fcphf1b1vz80iov4ivx3v.png" alt="Card number 10"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flo0gie5xvnkp8k6rsw21.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%2Flo0gie5xvnkp8k6rsw21.png" alt="Carb number 11"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq08wh32tubvqflz4jahh.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%2Fq08wh32tubvqflz4jahh.png" alt="Card number 12"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqj9h00unkr36mtfszlcw.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%2Fqj9h00unkr36mtfszlcw.png" alt="Card number 13"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd4qk7h0lempf04jrs0ph.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%2Fd4qk7h0lempf04jrs0ph.png" alt="Card number 14"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frd5j81que6m48ooqhif6.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%2Frd5j81que6m48ooqhif6.png" alt="Card number 15"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbsr8zx4ixwc3bui30a89.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%2Fbsr8zx4ixwc3bui30a89.png" alt="Card number 16"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkewinah6c84u6f2gzs2p.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%2Fkewinah6c84u6f2gzs2p.png" alt="Card number 17"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw57wjc13xdyyjis5zjt9.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%2Fw57wjc13xdyyjis5zjt9.png" alt="Card number 18"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>design</category>
      <category>ux</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to design almost any UI element. A curated list of 58 articles.</title>
      <dc:creator>Victor</dc:creator>
      <pubDate>Thu, 15 Apr 2021 11:55:01 +0000</pubDate>
      <link>https://dev.to/vponamariov/how-to-design-almost-any-ui-element-a-curated-list-of-58-articles-1m49</link>
      <guid>https://dev.to/vponamariov/how-to-design-almost-any-ui-element-a-curated-list-of-58-articles-1m49</guid>
      <description>&lt;p&gt;Hi there!&lt;/p&gt;

&lt;p&gt;Recently I posted &lt;a href="https://twitter.com/vponamariov/status/1381584285300359169" rel="noopener noreferrer"&gt;on Twitter&lt;/a&gt; a list of 58 links related to different UI elements. It turned out that the tweet was very helpful, but it's not very convenient to read the links as a thread.&lt;/p&gt;

&lt;p&gt;So I wanted to share them here, on dev.to, in a single list.&lt;/p&gt;




&lt;h2&gt;
  
  
  Buttons
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://uxplanet.org/7-basic-rules-for-button-design-63dcdf5676b4" rel="noopener noreferrer"&gt;7 Basic Rules for Button Design&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uxdesign.cc/button-design-user-interface-components-series-85243b6736c7" rel="noopener noreferrer"&gt;Button Design — UI component series&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uxdesign.cc/ui-cheat-sheets-buttons-7329ed9d6112" rel="noopener noreferrer"&gt;UI cheat sheet: buttons&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Text fields
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://uxdesign.cc/ui-cheat-sheet-text-fields-2152112615f8" rel="noopener noreferrer"&gt;UI cheat sheet: text fields&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uxdesign.cc/text-fields-forms-design-ui-components-series-2b32b2beebd0" rel="noopener noreferrer"&gt;Text fields &amp;amp; Forms design — UI components series&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uxplanet.org/the-anatomy-of-input-field-c3ef863e01d7" rel="noopener noreferrer"&gt;The Anatomy of Input Field&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Checkboxes &amp;amp; Toggles
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://uxplanet.org/checkbox-vs-toggle-switch-7fc6e83f10b8" rel="noopener noreferrer"&gt;Checkbox vs Toggle Switch&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nngroup.com/articles/toggle-switch-guidelines/" rel="noopener noreferrer"&gt;Toggle-Switch Guidelines&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.justinmind.com/blog/toggle-design-patterns-examples/" rel="noopener noreferrer"&gt;Toggle switch design: the full run through&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uicookies.com/css-checkbox/" rel="noopener noreferrer"&gt;38 Checkbox Designs&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Radio Buttons
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://uxdesign.cc/selection-controls-ui-component-series-3badc0bdb546" rel="noopener noreferrer"&gt;Selection controls — UI component series&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uxplanet.org/radio-buttons-ux-design-588e5c0a50dc" rel="noopener noreferrer"&gt;Radio Buttons UX Design&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.justinmind.com/blog/radio-button-design-examples/" rel="noopener noreferrer"&gt;Radio button design: easy selection and decision-making&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tables
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://medium.com/nextux/design-better-data-tables-4ecc99d23356" rel="noopener noreferrer"&gt;Design better data tables&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/design-with-figma/the-ultimate-guide-to-designing-data-tables-7db29713a85a" rel="noopener noreferrer"&gt;The Ultimate Guide to Designing Data Tables&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uxdesign.cc/data-table-for-enterprise-ux-cb48fb9fdf1e" rel="noopener noreferrer"&gt;Designing better data tables for enterprise UX&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Datepickers
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://uxplanet.org/how-to-design-a-perfect-date-picker-control-7f47d1290c3a" rel="noopener noreferrer"&gt;How to Design a Perfect Date Picker Control?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uxplanet.org/date-picker-design-best-practices-41bd522f10a5" rel="noopener noreferrer"&gt;Date Picker Design Best Practices&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uxdesign.cc/date-picker-design-5c5ef8f35286" rel="noopener noreferrer"&gt;A hunt for the perfect date picker UI&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sliders
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.smashingmagazine.com/2017/07/designing-perfect-slider/" rel="noopener noreferrer"&gt;Designing The Perfect Slider&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.justinmind.com/blog/slider-design-web/" rel="noopener noreferrer"&gt;Slider design UI patterns and examples&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nngroup.com/articles/gui-slider-controls/" rel="noopener noreferrer"&gt;Slider Design: Rules of Thumb&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tabs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.smashingmagazine.com/2009/06/module-tabs-in-web-design-best-practices-and-solutions/" rel="noopener noreferrer"&gt;Module Tabs in Web Design: Best Practices and Solutions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nngroup.com/articles/tabs-used-right/" rel="noopener noreferrer"&gt;12 Tabs Design guidelines&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dropdowns
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://uxdesign.cc/ui-cheat-sheet-dropdown-field-a30025c0f432" rel="noopener noreferrer"&gt;UI cheat sheet: dropdown field&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nngroup.com/articles/drop-down-menus/" rel="noopener noreferrer"&gt;Dropdowns: Design Guidelines&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.justinmind.com/blog/drop-down-list-design/" rel="noopener noreferrer"&gt;Drop down list design: the complete guide&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pagination
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.uxmatters.com/mt/archives/2018/11/paging-scrolling-and-infinite-scroll.php" rel="noopener noreferrer"&gt;Paging, Scrolling, and Infinite Scroll&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uxplanet.org/ux-infinite-scrolling-vs-pagination-1030d29376f1" rel="noopener noreferrer"&gt;UX: Infinite Scrolling vs. Pagination&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nngroup.com/articles/item-list-view-all/" rel="noopener noreferrer"&gt;Users' Pagination Preferences and "View All"&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Search inputs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://uxdesign.cc/best-ux-practices-for-search-inputs-c44dba565448" rel="noopener noreferrer"&gt;Best UX practices for search inputs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uxplanet.org/design-a-perfect-search-box-b6baaf9599c" rel="noopener noreferrer"&gt;Design a Perfect Search Box&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nngroup.com/articles/site-search-suggestions/" rel="noopener noreferrer"&gt;Site Search Suggestions&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Breadcrumbs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.nngroup.com/articles/breadcrumbs/" rel="noopener noreferrer"&gt;Breadcrumbs: 11 Design Guidelines for Desktop and Mobile&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/madison-ave-collective/are-breadcrumbs-still-fresh-for-ux-6e72b0f96e9b" rel="noopener noreferrer"&gt;Are breadcrumbs still fresh for UX?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.justinmind.com/blog/breadcrumb-examples/" rel="noopener noreferrer"&gt;Breadcrumb examples for inspiration&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Carousels
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://webflow.com/blog/carousel-slider-design-best-practices" rel="noopener noreferrer"&gt;Carousel/slider design best practices (with examples)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nngroup.com/articles/designing-effective-carousels/" rel="noopener noreferrer"&gt;Carousel Usability: Designing an Effective UI for Websites with Content Overload&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uxplanet.org/designing-a-user-friendly-homepage-carousel-f664c9f2b50e" rel="noopener noreferrer"&gt;Designing a User-Friendly Homepage Carousel&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Modals
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://xd.adobe.com/ideas/process/ui-design/best-practices-for-designing-overlays/" rel="noopener noreferrer"&gt;Best Practices for Designing UI Overlays&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.appcues.com/blog/modal-dialog-windows" rel="noopener noreferrer"&gt;Big, bold UX—using modal windows for in-app user engagement&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nngroup.com/articles/modal-nonmodal-dialog/" rel="noopener noreferrer"&gt;Modal &amp;amp; Nonmodal Dialogs: When (&amp;amp; When Not) to Use Them&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/pulsar/modern-enterprise-ui-design-part-2-modal-dialogs-2ccd3cc33c92" rel="noopener noreferrer"&gt;Modal dialogs&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cards
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.justinmind.com/blog/cards-ui-design/" rel="noopener noreferrer"&gt;Card UI design: fundamentals and examples&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uxplanet.org/simple-design-tips-for-crafting-better-ui-cards-19c1ac31a44e" rel="noopener noreferrer"&gt;Simple Design Tips for Crafting Better UI Cards&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uxdesign.cc/designing-cards-for-beginners-9ed9454d27f6" rel="noopener noreferrer"&gt;Designing cards&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tooltips
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.nngroup.com/articles/tooltip-guidelines/" rel="noopener noreferrer"&gt;Tooltip Guidelines&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.smashingmagazine.com/2021/02/designing-tooltips-mobile-user-interfaces/" rel="noopener noreferrer"&gt;Designing Better Tooltips For Mobile User Interfaces&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uxdesign.cc/tooltips-your-secret-weapon-for-improving-deature-discovery-e1c380562f2e" rel="noopener noreferrer"&gt;Tooltips: your secret weapon for improving feature discovery&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Navigation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.justinmind.com/blog/navigation-design-almost-everything-you-need-to-know/" rel="noopener noreferrer"&gt;Navigation design: Almost everything you need to know&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.prototypr.io/ux-design-for-navigation-menus-d9875c91a7ea" rel="noopener noreferrer"&gt;UX Design for Navigation Menu&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uxmovement.medium.com/the-fastest-navigation-layout-for-a-three-level-menu-b0480e2f11a2" rel="noopener noreferrer"&gt;The Fastest Navigation Layout for a Three-Level Menu&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Loaders
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.nngroup.com/articles/progress-indicators/" rel="noopener noreferrer"&gt;Progress Indicators Make a Slow System Less Insufferable&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uxdesign.cc/stop-using-a-loading-spinner-theres-something-better-d186194f771e" rel="noopener noreferrer"&gt;Stop Using A Loading Spinner, There’s Something Better&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.smashingmagazine.com/2016/12/best-practices-for-animated-progress-indicators/" rel="noopener noreferrer"&gt;Best Practices For Animated Progress Indicators&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Forms
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://medium.com/nextux/design-better-forms-96fadca0f49c" rel="noopener noreferrer"&gt;Design Better Forms&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cxl.com/blog/form-design-best-practices/" rel="noopener noreferrer"&gt;Form Design: 13 Empirically Backed Best Practices&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uxdesign.cc/the-ux-behind-designing-better-forms-d6ebe7a817d2" rel="noopener noreferrer"&gt;The UX behind designing better forms&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;That's it. Hopefully, it'll be useful for web devs. If you wish, &lt;a href="https://twitter.com/vponamariov" rel="noopener noreferrer"&gt;follow me on Twitter&lt;/a&gt;, I post a lot about UI/UX there.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>ux</category>
      <category>frontend</category>
    </item>
    <item>
      <title>4 Things you didn't know about text</title>
      <dc:creator>Victor</dc:creator>
      <pubDate>Tue, 23 Mar 2021 17:34:59 +0000</pubDate>
      <link>https://dev.to/vponamariov/4-things-you-didn-t-know-about-text-1d5k</link>
      <guid>https://dev.to/vponamariov/4-things-you-didn-t-know-about-text-1d5k</guid>
      <description>&lt;p&gt;Hi there! 👋&lt;/p&gt;

&lt;p&gt;Text is an essential part of most interfaces. It doesn’t matter if it’s a landing page or a web application.&lt;/p&gt;

&lt;p&gt;Here are four things you probably didn’t know about it. &lt;/p&gt;

&lt;p&gt;Understanding them might help you to make your texts look nicer and more balanced.&lt;/p&gt;

&lt;p&gt;Lest level up your UI skills 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  Rags
&lt;/h2&gt;

&lt;p&gt;A “rag” in typography is the uneven side of a paragraph where the text is aligned on the other side. So if the text is right-aligned, the rag is on the left side and vice-versa.&lt;/p&gt;

&lt;p&gt;A poor rag creates distracting shapes in the white space of the margin.&lt;/p&gt;

&lt;p&gt;Compare these two images.&lt;/p&gt;

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

&lt;p&gt;The left text feels less balanced than the right. To fix rags, you can adjust the text width and hyphenations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rivers
&lt;/h2&gt;

&lt;p&gt;Rivers are the white gaps that can appear in the text (especially justified ones) when there is too much space between words on consecutive text lines.&lt;/p&gt;

&lt;p&gt;Usually, they appear when you justify your text. So you can easily fix it by aligning the text to the left side.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Widows
&lt;/h2&gt;

&lt;p&gt;A widow is a paragraph-ending line that falls at the beginning of the following page or column, separated from the rest of the text.&lt;/p&gt;

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

&lt;p&gt;One of the possible solutions to avoid widows is to insert an early page break.&lt;/p&gt;

&lt;h2&gt;
  
  
  Orphans
&lt;/h2&gt;

&lt;p&gt;Orphan is a paragraph-opening line that appears at the bottom of a page, separated from the rest of the text.&lt;/p&gt;

&lt;p&gt;Orphans and widows are very similar to each other. &lt;/p&gt;

&lt;p&gt;I found controversial definitions and mnemonic rules to remember who is who. One of the funniest was “Widows have a past but no future, while orphans have no past but a future.”&lt;/p&gt;

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

&lt;p&gt;But it doesn’t matter much what do you call them. It’d be better if you fix them. Same as for widows, page break can help.&lt;/p&gt;

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

&lt;p&gt;These four problems described above make text un-balanced and look no that good as it could be.&lt;/p&gt;

&lt;p&gt;I’m pretty sure it won’t matter that much if you have a widow or rivers in the text on your landing page. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BUT REMEMBER&lt;/strong&gt;: Interface consists of many things. Everything can look good and bad. Everything can be convenient to work with and not that convenient. &lt;/p&gt;

&lt;p&gt;When we see an interface, we experience certain emotions. And if you cared about most things, it’d be a pleasure for the user.&lt;/p&gt;

&lt;p&gt;There is a special effect that describes this behavior: &lt;/p&gt;

&lt;p&gt;“The aesthetic–usability effect describes a paradox that people perceive more aesthetic designs as much more intuitive than those considered to be less aesthetically pleasing.”&lt;/p&gt;

&lt;p&gt;If you want to learn more, &lt;a href="https://twitter.com/vponamariov" rel="noopener noreferrer"&gt;follow me on Twitter&lt;/a&gt;, I tweet a lot about UI/UX stuff :)&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>design</category>
      <category>ux</category>
    </item>
    <item>
      <title>Interview questions. Are they wrong?</title>
      <dc:creator>Victor</dc:creator>
      <pubDate>Tue, 10 Nov 2020 11:20:39 +0000</pubDate>
      <link>https://dev.to/vponamariov/interview-questions-are-they-wrong-35dp</link>
      <guid>https://dev.to/vponamariov/interview-questions-are-they-wrong-35dp</guid>
      <description>&lt;p&gt;In this article, I'm going to share a controversial opinion about interview questions. I understand that I may be wrong, and I'm not claiming that this is 100% true for most cases.&lt;/p&gt;

&lt;p&gt;It's applied not only for web development but since I'm a web dev, I'll show examples based on my experience.&lt;/p&gt;

&lt;h1&gt;
  
  
  What's wrong with the interview questions?
&lt;/h1&gt;

&lt;p&gt;I've been working as a web dev for almost ten years. I've started working in 2011 when I graduated. My tech stack hasn't changed from that time. I still use PHP/MySQL &amp;amp; javascript mostly.&lt;/p&gt;

&lt;p&gt;From time to time, I hear about interview questions asked in small/mid-sized companies.&lt;/p&gt;

&lt;p&gt;And here is the confusion: while I've been developing web apps, I really haven't used or written such code samples as interviewers ask me to write or explain.&lt;/p&gt;

&lt;p&gt;I don't understand this. &lt;/p&gt;

&lt;p&gt;For example, a company develops some web apps, maybe a CRM or analytics apps. &lt;/p&gt;

&lt;p&gt;Nowadays, a lot of systems are written using frameworks.&lt;/p&gt;

&lt;p&gt;In 90% of cases, &lt;strong&gt;typical tasks which devs do are:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install framework&lt;/li&gt;
&lt;li&gt;Build UI with components&lt;/li&gt;
&lt;li&gt;Set handlers for UI elements&lt;/li&gt;
&lt;li&gt;Send requests using Axios or another library&lt;/li&gt;
&lt;li&gt;Provide feedback to the user, depending on the results of the requests&lt;/li&gt;
&lt;li&gt;Use some libs for common stuff, like charts/validation/date pickers/etc&lt;/li&gt;
&lt;li&gt;HTML/CSS stuff&lt;/li&gt;
&lt;li&gt;Working with external APIs&lt;/li&gt;
&lt;li&gt;etc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, I've rarely had to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deal with complex type conversion in javascript&lt;/li&gt;
&lt;li&gt;Deal with algorithms. I understand that for Google, or for really complex systems (if you make a game, develop 3D graphics, etc.), they a must. But not for a typical web app.&lt;/li&gt;
&lt;li&gt;Use prototypes. I use them but rarely&lt;/li&gt;
&lt;li&gt;Write tricky code when I don't know what "this" points to. When I started working with frameworks, I rarely had problems with "this"&lt;/li&gt;
&lt;li&gt;Remember that &lt;code&gt;typeof&lt;/code&gt; &lt;em&gt;something&lt;/em&gt; is &lt;em&gt;something not logical&lt;/em&gt;. Since javascript is not easy to remember and understand if we talk about types. I really forget all the time what's the type of array (Object?) and why NaN is not NaN&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's check some interview questions, and I'll be more clear.&lt;/p&gt;

&lt;h1&gt;
  
  
  Example questions
&lt;/h1&gt;

&lt;p&gt;I've just googled "tricky javascript questions," and here what I got. Companies might not ask exactly these questions, but they are in the same category, tricky questions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function foo() {
  let a = b = 0;
  a++;
  return a;
}

foo();
typeof a; // =&amp;gt; ???
typeof b; // =&amp;gt; ???
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've never in my life written &lt;code&gt;let a = b = 0&lt;/code&gt;. Non-readable, who knows how this works in javascript. &lt;/p&gt;

&lt;p&gt;Oh, "b" is global in this case? Nice. I didn't know about it. And didn't have to deal with it, never.&lt;/p&gt;

&lt;p&gt;Next.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const clothes = ['jacket', 't-shirt'];
clothes.length = 0;

clothes[0]; // =&amp;gt; ???
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm not sure I've ever changed the length of the array. I change the array itself, not its length. But in case I did change it and had some problems with it, I'd &lt;code&gt;console.log&lt;/code&gt; the result and know immediately what's going on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function arrayFromValue(item) {
  return
    [item];
}

arrayFromValue(10); // =&amp;gt; ???
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What? No comments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let i;
for (i = 0; i &amp;lt; 3; i++) {
  const log = () =&amp;gt; {
    console.log(i);  }
  setTimeout(log, 100);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one is a prevalent question. I always forget why it works that way, but I'm aware of this case. Not sure if I have ever written something like this, but after &lt;code&gt;let&lt;/code&gt; was introduced, it's not a problem.&lt;/p&gt;

&lt;p&gt;Let's go ahead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Why 018 - 017 equals 3?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NEVER used the octal system. Okay, guys who are attentive enough may notice the leading zero in the numbers. But another dev, who can be very responsible &amp;amp; experienced, might not notice this and might not remember that the octal system exists at all.&lt;/p&gt;

&lt;p&gt;Again, I don't claim that my opinion is absolute, immutable truth.&lt;/p&gt;

&lt;p&gt;But when I hired people, I asked only questions like these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is computed/methods/filters/watchers in vuejs?&lt;/li&gt;
&lt;li&gt;Which library do you use for working with API?&lt;/li&gt;
&lt;li&gt;Which API you worked with?&lt;/li&gt;
&lt;li&gt;OS experience, do you know Linux? Are you okay with bash?&lt;/li&gt;
&lt;li&gt;Do you have examples of your work?&lt;/li&gt;
&lt;li&gt;Which IDE / OS you use, comfortable with?&lt;/li&gt;
&lt;li&gt;HTTP verbs/headers/codes&lt;/li&gt;
&lt;li&gt;Ways of optimizing site? &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;etc&lt;/p&gt;

&lt;p&gt;I think I never ask "What the code will output". Do you want to know what the code will output? &lt;code&gt;console.log&lt;/code&gt; :)&lt;/p&gt;

&lt;p&gt;SAME for logical quizzes, you know all these quizzes about coins &amp;amp; scales etc.&lt;/p&gt;

&lt;p&gt;I never solved them, but I can write an HTTP request in a minute.&lt;/p&gt;

&lt;p&gt;Do you need a coin/scale-solver or you need to display results of an API request to your users?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Validate it. The right way.</title>
      <dc:creator>Victor</dc:creator>
      <pubDate>Tue, 03 Nov 2020 10:28:37 +0000</pubDate>
      <link>https://dev.to/vponamariov/validate-it-ultimate-guide-41d1</link>
      <guid>https://dev.to/vponamariov/validate-it-ultimate-guide-41d1</guid>
      <description>&lt;h1&gt;
  
  
  Table of contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Intro&lt;/li&gt;
&lt;li&gt;Composing error messages&lt;/li&gt;
&lt;li&gt;Selecting the right place&lt;/li&gt;
&lt;li&gt;
Validation modes

&lt;ul&gt;
&lt;li&gt;Aggressive&lt;/li&gt;
&lt;li&gt;Passive&lt;/li&gt;
&lt;li&gt;Lazy&lt;/li&gt;
&lt;li&gt;Eager&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Help your users&lt;/li&gt;

&lt;li&gt;

Practical examples

&lt;ul&gt;
&lt;li&gt;Facebook&lt;/li&gt;
&lt;li&gt;Steam&lt;/li&gt;
&lt;li&gt;Name.com&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Afterword&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Intro&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Forms are essential parts of any user interface. They differ from a single input and a button, ending with huge complex forms containing multiple sections.&lt;/p&gt;

&lt;p&gt;The validation process often accompanies forms. For developers, it might be clear how the form should be filled. &lt;/p&gt;

&lt;p&gt;But remember that there are so many circumstances you have to take into account.&lt;/p&gt;

&lt;p&gt;Your users can be children. They can be old. They can be tired. They can be drunk.&lt;/p&gt;

&lt;p&gt;They can fill the form from mobile. From tablet. From desktop. &lt;/p&gt;

&lt;p&gt;In a car. In a cafe. At home. &lt;/p&gt;

&lt;p&gt;They can even be drunk, in a car, with children, and filling the form from a tablet. 😊&lt;/p&gt;

&lt;p&gt;Once I worked in a company that specialized in real estate searching. They had a site where you could place an advertisement when you wanted to sell a house. &lt;/p&gt;

&lt;p&gt;I worked in the company as a UX-intern for a month. The &lt;strong&gt;whole&lt;/strong&gt; month we were researching how users fill the advertisement form and their mistakes. Just imagine, the whole month...😱&lt;/p&gt;

&lt;p&gt;At first, validation might look like a simple process.&lt;/p&gt;

&lt;p&gt;You find some javascript library, put validation rules, and that's it.&lt;/p&gt;

&lt;p&gt;However, validation is a very complex process in terms of user experience, which is often underestimated.&lt;/p&gt;

&lt;p&gt;The aim of validation is not to pass your data to the server correctly. It's about helping users fill it with less effort. Your server still needs validation. &lt;/p&gt;

&lt;p&gt;The main aim is to reduce &lt;a href="https://www.nngroup.com/articles/interaction-cost-definition/" rel="noopener noreferrer"&gt;interaction costs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here I want to show you some useful tips regarding the validation process.&lt;/p&gt;

&lt;h1&gt;
  
  
  Composing error messages &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;First of all, the messages themselves should be &lt;strong&gt;explicit&lt;/strong&gt;. This means that you should clearly indicate what's wrong with the data user entered.&lt;/p&gt;

&lt;p&gt;Bad example: "Your password is wrong."&lt;br&gt;
Good example: "Your password should contain at least 6 characters."&lt;/p&gt;

&lt;p&gt;Then, the messages should indicate &lt;strong&gt;what&lt;/strong&gt; user should do. This rule is applied to the whole UX area. When the user received a message, he shouldn't be left with no clues of what to do next.&lt;/p&gt;

&lt;p&gt;Bad example: "Something went wrong, sorry!"&lt;br&gt;
Good example: "Looks like someone has already used your email, &lt;strong&gt;please, use another email address&lt;/strong&gt;."&lt;/p&gt;

&lt;p&gt;I don't claim that my English is good. My purpose here is to show that there should be some indication of what to do next.&lt;/p&gt;

&lt;p&gt;This is quite common when you get an error message in an app, which says that something went wrong, but it doesn't provide any information on what to do next. Apps just leave users alone, and this doesn't help them to do their tasks.&lt;/p&gt;

&lt;p&gt;The messages should be human-readable, meaning that you write for users, not for machines.&lt;/p&gt;

&lt;p&gt;Bad example: "Ooops, the server responded with 500 code."&lt;br&gt;
Good example: "Something went wrong on our side, we already received the error &amp;amp; our team is working on it. If it's urgent, contact our support at &lt;a href="mailto:support@app.com"&gt;support@app.com&lt;/a&gt;."&lt;/p&gt;

&lt;p&gt;Again, I may be way too wordy here, but I want to transmit the main idea.&lt;/p&gt;

&lt;h1&gt;
  
  
  Selecting the right place &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpkshwhk5gbmp5pj8zyr5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpkshwhk5gbmp5pj8zyr5.png" alt="Alt Text" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are two main principles of showing validation errors: inline and after the user submits the form.&lt;/p&gt;

&lt;p&gt;In the case of inline validation, the error messages are shown next to the input fields. While in the case "after the user submits the form" it may vary: you can show all validation errors at once next to every input or show them in the beginning/at the end of the form.&lt;/p&gt;

&lt;p&gt;The best place to show validation errors is inline. The reasons are obvious:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User will notice it as soon as he makes a mistake&lt;/li&gt;
&lt;li&gt;He can fix it as soon as he notices it&lt;/li&gt;
&lt;li&gt;No need to scroll &amp;amp; find inputs which are filled incorrectly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When user types in a certain field, he is in the context of this field. So if we show him an inline validation error, we will not switch the context: he knows which field we are talking about.&lt;/p&gt;

&lt;p&gt;While if we show him all errors at once, it will result in high interaction costs.&lt;/p&gt;

&lt;p&gt;Compare these two approaches:&lt;/p&gt;

&lt;p&gt;Inline: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User types in email&lt;/li&gt;
&lt;li&gt;Gets validation error&lt;/li&gt;
&lt;li&gt;He knows that he has just typed his email, so he even doesn't have to read the message. If he sees something red around email input, then he knows that something's wrong with what he has just typed in&lt;/li&gt;
&lt;li&gt;Easily fixes it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All at once:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User types in email&lt;/li&gt;
&lt;li&gt;User types in the name&lt;/li&gt;
&lt;li&gt;User types in the phone number&lt;/li&gt;
&lt;li&gt;Presses submit &lt;/li&gt;
&lt;li&gt;Gets few validation errors. He has to read them to understand what he has typed wrongly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's clear that in the second case, we ask much more effort from him than in the first case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validation modes&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;One is the most important part of the validation process if &lt;strong&gt;when&lt;/strong&gt; to trigger validation errors.&lt;/p&gt;

&lt;p&gt;Here I love how vee-validate handles this: &lt;a href="https://logaretm.github.io/vee-validate/guide/interaction-and-ux.html" rel="noopener noreferrer"&gt;https://logaretm.github.io/vee-validate/guide/interaction-and-ux.html&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So there are basically four modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Aggressive: Triggered when the user presses a key on input. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Passive: Triggered when the form is submitted. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lazy: Triggered when the user leaves the input (on blur or change). &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Eager: This is a combination of aggressive and lazy, as it first validates when the user leaves the input (on blur or change), then if the input is invalid, it will behave aggressively until the input is valid again, and it will go back to being lazy.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, let's consider these modes from the UX point of view.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aggressive &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffi4ikchxer1duyc05e1m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffi4ikchxer1duyc05e1m.png" alt="Alt Text" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is one of the worst.&lt;/p&gt;

&lt;p&gt;In this case, when the user has just started typing, you immediately validate the input. Which in 99% of cases leads to the error being shown.&lt;/p&gt;

&lt;p&gt;But the user might actually know how to fill the input correctly. He can't do this instantly. &lt;/p&gt;

&lt;p&gt;In most cases, I wouldn't recommend this approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Passive &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7guipvphuu14p3i6ge9y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7guipvphuu14p3i6ge9y.png" alt="Alt Text" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This one is better.&lt;/p&gt;

&lt;p&gt;In this case, you show validation errors when the user presses submit. I believe for simple forms that contain a couple of inputs it may be applicable.&lt;/p&gt;

&lt;p&gt;But if you have a large form containing multiple inputs divided into different groups, don't use this mode.&lt;/p&gt;

&lt;p&gt;Imagine the user has copied bank account information from a paper to the form, as well as email, password, random other fields. And then after submitting the form, he gets a dozen errors? Looks scary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lazy &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhybajmr4ihaadkfq4a8a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhybajmr4ihaadkfq4a8a.png" alt="Alt Text" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Lazy mode, the validation is triggered when the user leaves the input.&lt;/p&gt;

&lt;p&gt;It is much less annoying than aggressive mode and, in my opinion, is pretty nice.&lt;/p&gt;

&lt;p&gt;The user finished typing, decided that he has typed everything correctly, and then switched to the next input. &lt;/p&gt;

&lt;p&gt;But this approach has one significant drawback. In case the user made a mistake and returned to the input to correct it, the validation will trigger only when he switches to the next input.&lt;/p&gt;

&lt;p&gt;Imagine the user typed the wrong email, returned to the input to correct it, corrected it but hasn't yet switched to another input and still sees the error. That's not nice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Eager &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The eager mode is a combination of aggressive and lazy, as it first validates when the user leaves the input. &lt;/p&gt;

&lt;p&gt;If the input is invalid, it will behave aggressively until the input is valid again and will go back to being lazy.&lt;/p&gt;

&lt;p&gt;So, how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User typed an email. While he was typing it, we do not validate his data&lt;/li&gt;
&lt;li&gt;User switched to another input. If he made a mistake in an email, we show him an error (like in lazy mode) and turn the input in aggressive mode.&lt;/li&gt;
&lt;li&gt;User returned to the email input and started fixing his mistake. As soon as he fixed the error, we remove the validation message. We don't wait till he leaves input as in lazy mode. We're in aggressive mode.&lt;/li&gt;
&lt;li&gt;Now, the input in lazy mode again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Looks pretty good.&lt;/p&gt;

&lt;h1&gt;
  
  
  Help your users to fill the form correctly &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5zi6u0w9cjun4b70t0vy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5zi6u0w9cjun4b70t0vy.png" alt="Alt Text" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should help your users to fill the form as much as you can. What can you do?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Break large forms into groups of inputs&lt;/li&gt;
&lt;li&gt;Indicate which fields are required&lt;/li&gt;
&lt;li&gt;Pre-fill inputs when possible&lt;/li&gt;
&lt;li&gt;Provide masks when possible (e.g., phone number format, dates, etc.)&lt;/li&gt;
&lt;li&gt;Provide hints. For example, when the user types in a password, which should have some complexity, you can always show the user how well he does.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Consider showing the user that he has filled an input correctly. &lt;/p&gt;

&lt;p&gt;For tiny forms, like the login form, it might not be necessary. However, if the form is complex, it might be a good idea to show users right away that he's doing well.&lt;/p&gt;

&lt;p&gt;Another example: you have a signup form. Your site is popular, so there is a high probability that the email is already taken.&lt;/p&gt;

&lt;p&gt;The user has entered the email and switched to the password field. And nothing happened. He might be a bit worried if the email is taken or not.&lt;/p&gt;

&lt;p&gt;To reduce possible stress, you can show right away if the email is acceptable or not.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvjk3gr4fdqhw1dyl17dr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvjk3gr4fdqhw1dyl17dr.png" alt="Alt Text" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Practical examples &lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;I'm not that good at writing. Instead, I'd better show some examples of poorly designed validation forms I found on the Internet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Facebook &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Here is the Facebook signup form.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0uuo1xnjdqs9zovhl7y8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0uuo1xnjdqs9zovhl7y8.png" alt="Alt Text" width="800" height="676"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Apart from making labels inside inputs, which is generally considered a bad practice, Facebook also hid validation error messages under icons.&lt;/p&gt;

&lt;p&gt;So to understand what I've done wrong, I need to click (not even hover) the icon.&lt;/p&gt;

&lt;p&gt;Instead, it'd be better to show the messages explicitly.&lt;/p&gt;

&lt;p&gt;Next, Facebook has special rules for user passwords. In case the user didn't follow the rules, he'll get the next error: "Enter a combination of at least six numbers, letters and punctuation marks (such as ! and &amp;amp;)."&lt;/p&gt;

&lt;p&gt;However, it'd be better to show how the user is doing when he's typing the password. Something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fg2qi5b64st2iuepri04s.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fg2qi5b64st2iuepri04s.jpg" alt="Alt Text" width="404" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Steam &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Let's check out Steam's register form:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsnwvo88b034xh6pcuee1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsnwvo88b034xh6pcuee1.png" alt="Alt Text" width="800" height="673"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay, so, a simple sign up form took the whole screen...🤔&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Why not tell me that I've typed the wrong email as soon as I did this?&lt;/li&gt;
&lt;li&gt;Why should I confirm the email address if, after submitting the form, you'll send me a verification link? It looks like I should verify my email twice for you?&lt;/li&gt;
&lt;li&gt;All errors are placed at the top in a single message. It takes time for me to look up at the error alert and then figure out what fields are wrong.&lt;/li&gt;
&lt;li&gt;"And find more errors highlighted below." Okay, you're a machine, why you require me to do the work of finding errors? Tell me right away. I understand that you highlighted the fields, but still... &lt;/li&gt;
&lt;li&gt;By the way, you haven't highlighted the captcha&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Name.com &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Honestly, I got no idea what's the site is about. I've just typed in "create your account" on google, and it seems like it's a popular site :)&lt;/p&gt;

&lt;p&gt;But it has quite a few validation errors.&lt;/p&gt;

&lt;p&gt;So, let's take a look at the form.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6dgd59ciibysdpw13zu3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6dgd59ciibysdpw13zu3.png" alt="Alt Text" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Woah, your form looks really big. It's okay. Sometimes we have to ask the user a lot of questions. But again, why not show validation errors as soon as I've filled an input?&lt;/p&gt;

&lt;p&gt;See, I've made so many mistakes: wrong phone number, wrong email, wrong zip code. After I click the submit button, you don't even bother to scroll me to the first invalid input!&lt;/p&gt;

&lt;p&gt;By the way, this is &lt;strong&gt;awesome&lt;/strong&gt; hint: if the user clicks submit and some inputs are wrong, scroll him to the first error! There are plenty of solutions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/logaretm/vee-validate/issues/2395" rel="noopener noreferrer"&gt;https://github.com/logaretm/vee-validate/issues/2395&lt;/a&gt;&lt;br&gt;
&lt;a href="https://stackoverflow.com/questions/60870424/vuetify-vee-validate-scroll-to-the-first-validation-error" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/60870424/vuetify-vee-validate-scroll-to-the-first-validation-error&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But back to the form. Here is a list of mistakes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Show errors inline, as soon as the user has finished entering data in an input&lt;/li&gt;
&lt;li&gt;Provide masks for phone number/zip code. If possible.&lt;/li&gt;
&lt;li&gt;My own preference: I'd make the form more compact, way too large.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Good thing you show me how good I am at entering my password though!&lt;/p&gt;

&lt;h1&gt;
  
  
  Afterword
&lt;/h1&gt;

&lt;p&gt;The main thing you should take from this article: always follow &lt;strong&gt;common sense&lt;/strong&gt;. EVERY rule can be broke. There are really MANY circumstances which you should consider.&lt;/p&gt;

&lt;p&gt;Your users are developers who are using vim for writing their essays? They use the console browser, maybe lynx? Oh well... &lt;/p&gt;

&lt;p&gt;Or maybe your users are old citizens? Then make your inputs BIGGER. They might not have a good vision.&lt;/p&gt;

&lt;p&gt;Or maybe your users are children? Then make your inputs colorful. They will entertain them!&lt;/p&gt;

&lt;p&gt;Just think about who your users are and research how they might use your app.&lt;/p&gt;

&lt;p&gt;Thank you, Victor&lt;/p&gt;

&lt;p&gt;P.S. I tweet about UI/UX stuff from time to time &lt;a href="https://twitter.com/vponamariov" rel="noopener noreferrer"&gt;https://twitter.com/vponamariov&lt;/a&gt; 😊&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ux</category>
      <category>design</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Typical interface mistakes developers make</title>
      <dc:creator>Victor</dc:creator>
      <pubDate>Sat, 24 Oct 2020 11:37:33 +0000</pubDate>
      <link>https://dev.to/vponamariov/typical-interface-mistakes-developers-make-13ni</link>
      <guid>https://dev.to/vponamariov/typical-interface-mistakes-developers-make-13ni</guid>
      <description>&lt;p&gt;I’ve been working on web applications for more than ten years now. As a developer, it was always hard for me to make my interfaces look good and user friendly. But I still wanted them to be so.&lt;/p&gt;

&lt;p&gt;Now, as a solo founder, I realize that you might not have money for a designer. However, in order to make decent interfaces, it’s not necessary to have one.&lt;/p&gt;

&lt;p&gt;It’s now popular to talk about people who don’t have CS degrees but have become developers. I’m convinced that the same applies to developers who want to build beautiful interfaces: you don’t need a design degree.&lt;/p&gt;

&lt;p&gt;Here are some of the most frequent mistakes I meet when I browse random web apps.&lt;/p&gt;

&lt;h1&gt;
  
  
  Complexity over simplicity
&lt;/h1&gt;

&lt;p&gt;It often happens that developers design interfaces for their convenience. They might be quite complicated but easy to use for their creator.&lt;/p&gt;

&lt;p&gt;While the title seems to be way too abstract, I can show some examples of what I mean by complexity over simplicity.&lt;/p&gt;

&lt;p&gt;For example, sometimes devs try to fill the empty space in their app or provide some additional hints which aren’t necessary.&lt;/p&gt;

&lt;p&gt;Remember: a good interface doesn’t need an explanation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5cd4r821lz4zzbb9xsl7.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%2Fi%2F5cd4r821lz4zzbb9xsl7.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sometimes people use excessive wording, e.g., “Your Email” while it is obvious that the app asks for your email, so you can simply put it as “Email”.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have a large form? Break it into groups.&lt;/li&gt;
&lt;li&gt;Have a large number of groups? Make a wizard, breaking the * process of filling the form into steps.&lt;/li&gt;
&lt;li&gt;Need to provide a broad explanation? Put it into a popup, which is hidden by default.&lt;/li&gt;
&lt;li&gt;Have complex filtering or advanced settings? Hide them by default&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Wrong grouping
&lt;/h1&gt;

&lt;p&gt;One of the essential things in user interfaces is the rule of grouping. I don’t know if there is a particular term for this, but here is the illustration which will help you understand what I mean.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F699y2ke0is9fg0uebaye.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%2Fi%2F699y2ke0is9fg0uebaye.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have two logical groups of elements, the distance between these groups should be bigger than the distance of the elements inside these groups.&lt;/p&gt;

&lt;p&gt;While it may seem pretty obvious, I often come across interfaces that violate this principle. A few days ago, a fellow asked me to check his design. Here is an example of a violation&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6xu0zwkryo01sft8t6na.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%2Fi%2F6xu0zwkryo01sft8t6na.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the “Features” label is placed between two blocks. While we read from top to bottom, we can guess that the features come after the title, but it still takes some minimal amount of time to verify this. &lt;/p&gt;

&lt;p&gt;In complex applications, it can be worse.&lt;/p&gt;

&lt;p&gt;So pay special attention to the margins and paddings. Even if it’s a matter of a couple of pixels, our eye can catch this.&lt;/p&gt;

&lt;h1&gt;
  
  
  A large number of elements inside one group
&lt;/h1&gt;

&lt;p&gt;Our mind cannot operate a large number of elements inside one group. This may be applied to anything: starting from buttons ending with menu links. &lt;/p&gt;

&lt;p&gt;Do you remember the days when the interfaces looked like this?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgrrdxs8xrs3vb9d2dv1g.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%2Fi%2Fgrrdxs8xrs3vb9d2dv1g.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a good illustration of the violation of two principles: wrong spacing between groups and many elements inside one group.&lt;/p&gt;

&lt;p&gt;Right now I’m writing this article in Google docs. The toolbar is broken into logical groups. Each group does not contain many elements, up to 5-7. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpo18ts3hljcw2lpatc51.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%2Fi%2Fpo18ts3hljcw2lpatc51.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, if you have a group of elements that contain too many elements (more than 6-7), then try to break them into separate groups.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lack of contrast
&lt;/h1&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%2Fi%2F791r0zruu5uk7zz8fiyb.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%2Fi%2F791r0zruu5uk7zz8fiyb.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The picture above demonstrates the problem with contrast. You can often read a text inside a button, but you don’t realize that it has low contrast. You just got used to the text, but new users will feel some tension.&lt;/p&gt;

&lt;p&gt;Sometimes this is pretty easy to fix. When you’re developing your site in a web browser, you can check contrast using dev tools.&lt;/p&gt;

&lt;h1&gt;
  
  
  Not enough attention to UX
&lt;/h1&gt;

&lt;p&gt;There are plenty of examples when people have nice looking interfaces but forget about the UX side. &lt;/p&gt;

&lt;p&gt;This keyboard has “power” and “delete” buttons next to each other. But people often use the del button, while almost don’t use the power button. So you can accidentally click on the wrong button and turn your laptop off.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frikapd6642cjyvxrjtm9.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%2Fi%2Frikapd6642cjyvxrjtm9.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everybody knows how it’s frustrating to work with Excel cell formatting...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1g7lr260lejwbsob3378.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%2Fi%2F1g7lr260lejwbsob3378.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not everyone does UX research before designing their interfaces…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Facpdg4dgq5ci490oktfu.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%2Fi%2Facpdg4dgq5ci490oktfu.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So these are just some examples of typical mistakes I can think of for this article. &lt;/p&gt;

&lt;p&gt;If you want to get more such stuff, check out my Twitter &lt;a href="https://twitter.com/vponamariov" rel="noopener noreferrer"&gt;https://twitter.com/vponamariov&lt;/a&gt; :)&lt;/p&gt;

&lt;p&gt;Cheers,&lt;br&gt;
Victor.&lt;/p&gt;

</description>
      <category>design</category>
      <category>ux</category>
      <category>ui</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I built architecture of uptime monitoring service</title>
      <dc:creator>Victor</dc:creator>
      <pubDate>Thu, 17 Sep 2020 18:47:56 +0000</pubDate>
      <link>https://dev.to/vponamariov/how-i-built-architecture-of-uptime-monitoring-service-54h8</link>
      <guid>https://dev.to/vponamariov/how-i-built-architecture-of-uptime-monitoring-service-54h8</guid>
      <description>&lt;h1&gt;
  
  
  Table of contents
&lt;/h1&gt;

&lt;p&gt;Intro&lt;br&gt;
Requirements&lt;br&gt;
VPS Provider and Servers&lt;br&gt;
Technologies used&lt;br&gt;
How a node works&lt;br&gt;
How the core server works&lt;br&gt;
To sum up&lt;br&gt;
What didn't work&lt;br&gt;
Why it took so long?&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;Intro&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Hi there! &lt;/p&gt;

&lt;p&gt;This is my first article so it might not be perfect 😇&lt;/p&gt;

&lt;p&gt;Almost a year ago I decided to build an uptime monitoring service, which resulted in &lt;a href="https://pingr.io" rel="noopener noreferrer"&gt;https://pingr.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is a web app that continuously checks that your URL responds with 200 OK or any other response code you prefer.&lt;/p&gt;

&lt;p&gt;So the idea is pretty straightforward. You might think that it's not that hard to make an HTTP request after all.&lt;/p&gt;

&lt;p&gt;But it took me a year. 😱 &lt;/p&gt;

&lt;p&gt;In this article I want to describe how it works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I don't claim that the architecture is good.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;The architecture is somewhat the compromise between launching your product asap and having excellent architecture. &lt;/p&gt;

&lt;p&gt;I didn't want to spend yet another year of making it perfect, since it's pretty hard to keep motivation during the development.&lt;/p&gt;

&lt;p&gt;I'd love to hear what can be improved and where I did it completely wrong.&lt;/p&gt;

&lt;p&gt;Let's dive in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;At first, it seems like it's an easy task to make an HTTP request. However, there are a lot of requirements for what the service should do. Here are some of them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The URL should be checked from multiple nodes distributed around the world.&lt;/li&gt;
&lt;li&gt;Minimum check frequency is 1 minute&lt;/li&gt;
&lt;li&gt;You should be able to add new nodes&lt;/li&gt;
&lt;li&gt;Every node should make a decent number of HTTP requests per minute. At first, I was okay with 500-1000. But when the user number grows, it might be much more.&lt;/li&gt;
&lt;li&gt;Apart from HTTP checks, the service should also check SSL certificates (if they are valid/expires soon, etc.)&lt;/li&gt;
&lt;li&gt;The UI showing URL status should be updated in realtime&lt;/li&gt;
&lt;li&gt;The user should be notified of uptime/downtime events via different ways depending on his choice&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;VPS Provider and Servers&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I think that if you want to have your product up and running as soon as possible, it's okay to pay more for services which you you are comfortable with.&lt;/p&gt;

&lt;p&gt;So I tried both ScaleWay and Digital Ocean but eventually moved all my servers to Digital Ocean since I liked it more.&lt;/p&gt;

&lt;p&gt;What I have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ubuntu VPS with MySQL.  3 GB / 1 vCPU&lt;/li&gt;
&lt;li&gt;5 Ubuntu VPS which are located in different parts of the world. 3 GB / 1 vCPU&lt;/li&gt;
&lt;li&gt;Ubuntu VPS for the core server.  4 GB / 2 vCPUs&lt;/li&gt;
&lt;li&gt;Redis database:  1 GB RAM / 1vCPU&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Regarding MySQL. In the beginning, I used their RDS, since it's meant to be used as a database, and everything should've been cool. But when I wanted to change my.cnf I realized that I couldn't do that. And I like to have control of everything, at least in the beginning while I'm learning.&lt;/p&gt;

&lt;p&gt;So, for now, I decided to have just a VPS with MySQL installed since it gives me more control.&lt;/p&gt;

&lt;p&gt;Why MySQL? No idea. I've just worked only with this database.&lt;br&gt;
Why Ubuntu? No idea. I've just worked only with this OS. 😝&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;Technologies I used&lt;/a&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Laravel for backend&lt;/li&gt;
&lt;li&gt;VueJS for frontend&lt;/li&gt;
&lt;li&gt;MySQL for Database&lt;/li&gt;
&lt;li&gt;Redis for queues&lt;/li&gt;
&lt;li&gt;cURL for sending requests (Guzzle library)&lt;/li&gt;
&lt;li&gt;Supervisord for keeping my workers up and running&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;li&gt;PROFIT!&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;Listing of artisan commands&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;When I was writing this article, I realized that it might help a lot if I list all the Laravel's commands which I use here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;php artisan monitor:run-uptime-checks {--frequency=1}&lt;/code&gt; - Dispatches uptime checks jobs. Ran by cron every &lt;code&gt;frequency&lt;/code&gt; minutes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;php artisan checks:push&lt;/code&gt; - Fetches check results from the local Redis database to the temporary MySQL table. Kept running continuously by Supervisord&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;php artisan checks: pull&lt;/code&gt; - Fetchs check results from the temporary MySQL table and calculates monitor status/uptime/other indexes. Kept running continuously by Supervisord.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;How a node works&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This is one of the most important parts of the article, which describes how a single node works.&lt;/p&gt;

&lt;p&gt;I should note that I have a &lt;code&gt;Monitor&lt;/code&gt; entity, representing a URL that should be checked.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting monitors ready for uptime check
&lt;/h3&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%2Fi%2Fhfqrdl646lify2zu55sn.jpg" 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%2Fi%2Fhfqrdl646lify2zu55sn.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;php artisan monitor:run-uptime-checks&lt;/code&gt; command fetches monitors from the database based on some conditions. &lt;/p&gt;

&lt;p&gt;One of the requirements is uptime check frequency, which means how often we should check the monitor. Not every user wants to check their sites every minute.&lt;/p&gt;

&lt;p&gt;Then, using &lt;a href="https://laravel.com/docs/8.x/scheduling" rel="noopener noreferrer"&gt;Laravel scheduling mechanisim&lt;/a&gt;, it's easy to setup running this command with different frequencies.&lt;/p&gt;

&lt;p&gt;Passing frequency as an argument helps me to get only the monitors I need to check, depending on what frequency the user has set.&lt;/p&gt;

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

// In the RunUptimeChecks command, we fetch monitors by frequency specified by the user

$schedule-&amp;gt;command(RunUptimeChecks::class, ['--frequency=1'])
         -&amp;gt;everyMinute();

$schedule-&amp;gt;command(RunUptimeChecks::class, ['--frequency=5'])
         -&amp;gt;everyFiveMinutes();

$schedule-&amp;gt;command(RunUptimeChecks::class, ['--frequency=10'])
        -&amp;gt;everyTenMinutes();

$schedule-&amp;gt;command(RunUptimeChecks::class, ['--frequency=15'])
         -&amp;gt;everyFifteenMinutes();

$schedule-&amp;gt;command(RunUptimeChecks::class, ['--frequency=30'])
         -&amp;gt;everyThirtyMinutes();

$schedule-&amp;gt;command(RunUptimeChecks::class, ['--frequency=60'])
         -&amp;gt;hourly();


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Filling up the queue
&lt;/h3&gt;

&lt;p&gt;Then I put the uptime check jobs into the Redis queue for every monitor I have. This is what happens in &lt;code&gt;RunUptimeChecks&lt;/code&gt; command:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

foreach ($monitors as $monitor) {
    RunUptimeCheck::dispatch(
        (object) $monitor-&amp;gt;toArray(), 
        $node-&amp;gt;id
   );
}


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

&lt;/div&gt;

&lt;p&gt;👉 You may notice here something strange: (object) $monitor-&amp;gt;toArray(). &lt;/p&gt;

&lt;p&gt;At first, I passed the Monitor model to the job. However, there is a significant difference: &lt;strong&gt;when you pass a model to a job, it stores just a model id in the queue. Then, when the job is being executed, Laravel connects to the database to fetch the model, which resulted in hundreds of unnecessary connections&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is why I passed an object instead of the actual model, which serialized quite well. &lt;/p&gt;

&lt;p&gt;Another possible approach is removing the &lt;code&gt;SerializesModels&lt;/code&gt; trait from the job, which also might work, but I haven't tried that.&lt;/p&gt;

&lt;p&gt;So after this operation, we have some number of jobs in the queue ready to be executed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running uptime checks
&lt;/h3&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%2Fi%2F5r17ws5gcftfzeizuprb.jpg" 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%2Fi%2F5r17ws5gcftfzeizuprb.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To execute the jobs, we need to run the &lt;code&gt;php artisan queue:work&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;What we also need is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Many instances of the command running, because we need to do as many checks as we can per minute&lt;/li&gt;
&lt;li&gt;If the command fails, we need to revoke it and run again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For this purpose, I used &lt;a href="https://laravel.com/docs/8.x/queues#supervisor-configuration" rel="noopener noreferrer"&gt;Supervisord&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What it does is it spawns N number of PHP processes, every process is &lt;code&gt;queue:work&lt;/code&gt; command. If it fails, Supervisord will rerun it.&lt;/p&gt;

&lt;p&gt;Depending on VPS Memory and CPU cores, we might vary the number of processes. Thus, we can increase the number of uptime checks performed per minute.&lt;/p&gt;

&lt;h3&gt;
  
  
  Storing the temporary result
&lt;/h3&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%2Fi%2Fvqrn6hm9nryywv10xjyt.jpg" 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%2Fi%2Fvqrn6hm9nryywv10xjyt.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, after we ran a check, we need to store it somewhere. &lt;/p&gt;

&lt;p&gt;First, I store the check result in the local Redis database.&lt;br&gt;
Since there might be many processes that continuously push data to the queue, it fits perfectly for this purpose, since Redis is an in-memory database and is very fast.&lt;/p&gt;

&lt;p&gt;Then I have another command &lt;code&gt;php artisan checks:push&lt;/code&gt; that fetches the checks from the Redis database and does the batch insert into the &lt;code&gt;raw_checks&lt;/code&gt; MySQL table.&lt;/p&gt;

&lt;p&gt;So I got two tables: &lt;code&gt;monitor_checks&lt;/code&gt; and &lt;code&gt;raw_checks&lt;/code&gt;. The first one contains the latest successful check of a monitor and all of its failed checks. I do not store every check of every node per minute, since it'll result in billions of records and it doesn't provide much value for end-users.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;raw_checks&lt;/code&gt; table serves as a bridge between the core server and all nodes servers.&lt;/p&gt;

&lt;p&gt;After each check we need to do a lot of stuff:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Recalculate uptime of every node&lt;/li&gt;
&lt;li&gt;Recalculate the average response time of every node&lt;/li&gt;
&lt;li&gt;Recalculate monitors uptime/response time based on nodes info&lt;/li&gt;
&lt;li&gt;Send notifications if needed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It would be much more reasonable to fetch many checks at once and do all calculations on a server located close to the MySQL server.&lt;/p&gt;

&lt;p&gt;For example, the connection between the Indian node and the MySQL server located in Germany is relatively slow.&lt;/p&gt;

&lt;p&gt;So the aim is to make the nodes as independent as they can be. &lt;/p&gt;

&lt;p&gt;All they do regarding MySQL connection is: fetch monitors which should be checked &amp;amp; store results of checks. That's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;How the core server works&lt;/a&gt;
&lt;/h2&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%2Fi%2Fy32jv3r4wcu5p1ch5ps6.jpg" 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%2Fi%2Fy32jv3r4wcu5p1ch5ps6.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the core server, I run the &lt;code&gt;php artisan:checks-pull&lt;/code&gt; command, which behaves like a daemon: it has an infinite loop that fetches checks from the &lt;code&gt;raw_checks&lt;/code&gt; table and calculates such things as average uptime, response time, and some others.&lt;/p&gt;

&lt;p&gt;Apart from that, it is responsible for queueing jobs for downtime notifications.&lt;/p&gt;

&lt;p&gt;That's actually it: we have monitors with updated status, uptime &amp;amp; response time attributes.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;Updating realtime data&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;In order to update monitor status on the web app, I use &lt;a href="http://pusher.com/" rel="noopener noreferrer"&gt;Pusher&lt;/a&gt; and &lt;a href="https://laravel.com/docs/8.x/broadcasting#introduction" rel="noopener noreferrer"&gt;Laravel's broadcasting&lt;/a&gt; feature.&lt;/p&gt;

&lt;p&gt;So the setup is straightforward.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;php artisan checks:pull&lt;/code&gt; gets a check from the raw table, sees if the monitor is online, and if it's not, it fires the &lt;code&gt;MonitorOffline&lt;/code&gt; event, which is broadcasted using Pusher.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Web app sees the new event from Pushed and marks the monitor as offline&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;To sum up&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;So, to sum up:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every node has a cron job, which fetches the monitor list and put the uptime check jobs into the local Redis queue&lt;/li&gt;
&lt;li&gt;A lot of threads ran by Supevrisord check the queue and make the HTTP requests&lt;/li&gt;
&lt;li&gt;The result is stored in Redis&lt;/li&gt;
&lt;li&gt;Then bunch of checks stored in local Redis moved to the raw checks table in the MySQL server&lt;/li&gt;
&lt;li&gt;The core server fetches the checks and do the calculations.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every node has the same mechanism. Now, having a constant stream of checks from the raw table, the core server can do many things like calculated average response time from a node, etc.&lt;/p&gt;

&lt;p&gt;If I want to extend the number of nodes, I'll just clone the node server, do some small configuration, and that's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;What didn't work&lt;/a&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;At first, I stored ALL checks in the database. Both failed and successful. It resulted in billions of records. But users mostly don't need it. Now instead, I have an aggregation table which stores uptime and response time by an hour&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;At first, the uptime check job did all the logic itself: I didn't have any temporary/bridge databases. So it connected to the MySQL server, calculated new uptime etc. Which immediately didn't work as soon as the number of monitors increased up to ~100. Because every check job did maybe 10-20 queries. 10-20 queries * 5 nodes * 100 monitors. So yeah, it wasn't scalable at all&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using a dedicated Redis server instead of the &lt;code&gt;raw_checks&lt;/code&gt; table. Since the raw checks table behaves like a cache, it might be reasonable to consider using Redis for this purpose. But for some reason, I kept losing checks data. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I tried both the sub/pub features of Redis and just storing the data. So I gave up and used a mechanism that is familiar for me: MySQL.&lt;/p&gt;

&lt;p&gt;Also, I think I've read somewhere that Redis is not the best solution if we need 100% confidence in storing data.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a&gt;Why did it take so long?&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Because of multiple factors.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I work at a full-time job, so I could work only in the evenings.&lt;/li&gt;
&lt;li&gt;I haven't had such experience before. Every project is a unique one.&lt;/li&gt;
&lt;li&gt;I had some psychological problems which I've described in &lt;a href="https://medium.com/@victor.ponamariov/how-therapy-turned-out-to-be-my-key-to-consistency-as-a-solo-founder-dbc48e1cbc9d" rel="noopener noreferrer"&gt;this article&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;I've checked many different ways of making this work with a high number of monitors. So I was kind of rebuilding the same thing many times&lt;/li&gt;
&lt;li&gt;Take into account that I also built a UI and did everything alone.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>showdev</category>
      <category>laravel</category>
      <category>startup</category>
    </item>
  </channel>
</rss>
