<?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: Piotr Gołofit</title>
    <description>The latest articles on DEV Community by Piotr Gołofit (@pgolofit).</description>
    <link>https://dev.to/pgolofit</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%2F819123%2F72d06030-10cc-49bc-8fc9-80ee279cae97.jpg</url>
      <title>DEV Community: Piotr Gołofit</title>
      <link>https://dev.to/pgolofit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pgolofit"/>
    <language>en</language>
    <item>
      <title>A brief history of PHP language &amp; what we can learn from it?</title>
      <dc:creator>Piotr Gołofit</dc:creator>
      <pubDate>Fri, 11 Apr 2025 10:05:02 +0000</pubDate>
      <link>https://dev.to/accesto/a-brief-history-of-php-language-what-we-can-learn-from-it-3hkl</link>
      <guid>https://dev.to/accesto/a-brief-history-of-php-language-what-we-can-learn-from-it-3hkl</guid>
      <description>&lt;p&gt;This year PHP gets into its thirties 🎉 And on this occasion I would like to briefly summarise the history of PHP web development, to better understand the present and the future of the PHP programming language. I am pretty sure there is a lot we can learn from this story. &lt;/p&gt;

&lt;p&gt;So let's begin...&lt;/p&gt;

&lt;p&gt;It was in June 1995, thirty years ago... or wait, let's start even a bit earlier...&lt;/p&gt;

&lt;h2&gt;
  
  
  How did it all start?
&lt;/h2&gt;

&lt;p&gt;It is November 1968 in frosty Greenland, when the future programmer Rasmus Lerdorf is born. The World Wide Web is still 21 years from being invented. And Greenland is still part of the Kingdom of Denmark, and no one is trying to buy it 😉 &lt;/p&gt;

&lt;p&gt;Would his parents know that Rasmus will become a programmer? Probably not, as the C programming language will be unknown for another few years. &lt;/p&gt;

&lt;p&gt;Fast forward to 1994. Rasmus Lerdorf is working on his online resume. Yes, online, because the World Wide Web was invented in 1989 and became open to the public in 1993. He is probably one of the first to have a digital CV 💪&lt;/p&gt;

&lt;p&gt;Rasmus wanted to track how many people had visited his resume. How would you do it back then? It was way before Google Analytics was launched. Heck, Google itself would be unknown for another four years! So what did he do? He coded himself a simple set of Common Gateway Interface (CGI) binaries first in Pearl and later in the C programming language, which eventually led to PHP being born. Here is his story.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"PHP began life as a simple little cgi wrapper written in Perl. I wrote it in an afternoon during a period between contracts when I needed a quick tool to get an idea of who was reading my online resume. It was never intended to go beyond my own private use. The web server where I had my resume was extremely overloaded and had constant problems forking processes. I rewrote the Perl wrapper in C to get rid of the considerable overhead of having to fork Perl each time my resume was accessed."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;~Rasmus Lerdorf (source: &lt;a href="https://www.php.net/manual/phpfi2.php" rel="noopener noreferrer"&gt;https://www.php.net/manual/phpfi2.php&lt;/a&gt;)&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm93c29py94w0wlrlfkze.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm93c29py94w0wlrlfkze.png" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  From PHP Tools to PHP Foundation
&lt;/h2&gt;

&lt;p&gt;Later more people using the same Web Server contacted Rasmus if they could use his wrapper. And as usual, if you give something to people, they keep asking for more. So Rasmus added more and more features to his toolset leading eventually to  a semi-complete distribution along with documentation, a mailing list and a FAQ.  How did he name it? A "Personal Home Page Tools", or in short, a PHP Toolset. &lt;/p&gt;

&lt;h3&gt;
  
  
  PHP/FI (aka PHP 1)
&lt;/h3&gt;

&lt;p&gt;Back then, Rasmus also began experimenting with databases and developed a tool for seamlessly integrating SQL queries into web pages. This solution, essentially a CGI wrapper, parsed SQL queries and simplified the creation of forms and tables based on those queries. He named this tool a Form Interpreter or FI for short. And that's how a PHP/FI was born. By many called a &lt;strong&gt;PHP v1&lt;/strong&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  PHP 2
&lt;/h3&gt;

&lt;p&gt;In 1997 the PHP acronym was formally changed to PHP: HyperText Preprocessor. In the second version of PHP/FI 2.0, Rasmus has completely rewritten the PHP and FI packages and combined them into a single program. It also evolved to the point, where it was a simple scripting language embedded inside HTML files. That's probably the moment where the term "dynamic web pages" was born, as PHP enriched static HTML sites with server-side generated content. And in this very way, it was to be known for many years to come, until web development got into templating and finally APIs and SPA applications. &lt;/p&gt;

&lt;h3&gt;
  
  
  PHP 3
&lt;/h3&gt;

&lt;p&gt;In 1997, two Israeli students from Tel Aviv - Andi Gutmans and Zeev Suraski — reached Rasmus online regarding his implementation of PHP. They wanted to use it for their university project, but it was simply inefficient and lacking some features. So they started discussions around various aspects of the current implementation and their redevelopment of PHP. And that's how PHP version 3 was born. It introduced included object-oriented programming and was the first version that more or less resembles PHP as it exists today. Two years later Zeev and Andi would combine their first names to register their new company - Zend Technologies. You might have heard of it 😉 &lt;/p&gt;

&lt;h3&gt;
  
  
  PHP 4
&lt;/h3&gt;

&lt;p&gt;May 2000 brought us PHP version 4. It was the one which, among other features, introduced HTTP sessions and output buffering. Andi Gutmans and Zeev Suraski started working on it just after the official release of v3. Their goal was to improve PHP and its performance for larger and more complex applications. The result of their work was a release of Zend Engine 1.0 which became the core of PHP 4.&lt;/p&gt;

&lt;p&gt;Thanks to OOP introduced in version 3 and new features from version 4, PHP became something more than just a simple scripting language for dynamic web pages. It evolved from a hypertext preprocessor to a fully fledged programming language for building more and more popular dynamic web applications and API's. It was around this time when the first ever RESTful API started to appear. &lt;/p&gt;

&lt;p&gt;PHP 4 was also the first one that powered... WordPress 1.0! The very first public version of this popular CMS was released in May 2003 when most of the servers were running on PHP 4.1. This was probably one of the crucial moments in the history of PHP. It was also the spark that led to a famous statistic saying that PHP powered over 80% of the whole internet. A year later, a young Harvard student Mark Zuckenber (ever heard of him? 😉) will code &lt;strong&gt;thefacebook.com&lt;/strong&gt; using PHP 4. &lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;First logos of Facebook (named TheFaceBook back then) and WordPress from 2003/2004&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  PHP 5
&lt;/h3&gt;

&lt;p&gt;Quick jump to July 2004 and release of PHP v5. By this point in time PHP's development community comprises dozens of developers, along with numerous others contributing to PHP-related projects such as PEAR, PECL, and documentation. It is a huge success for PHP and it is believed that this programming language is currently installed on even hundreds of millions of domains worldwide.&lt;/p&gt;

&lt;p&gt;PHP 5 represented a fundamental redesign of the language, featuring the new Zend Engine that improved performance while expanding object-oriented programming features with better syntax and functionality. It also added innovations like exception handling and enhanced error reporting. These advancements helped cement PHP's status as a powerful and widely used language for web development.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;The first version of Symfony released in 2005 extended PHP code with many useful components&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The year 2004 and the release of PHP 5 was also a turning point for a passionate Perl developer Fabien Potencier. That's exactly when he decided to switch his focus to PHP and created the Symfony framework project (released a year later) to help his company leverage the power of PHP for complex web applications.&lt;/p&gt;

&lt;p&gt;And what may surprise you, as of April 2025, still more than &lt;a href="https://w3techs.com/technologies/details/pl-php" rel="nofollow noopener noreferrer"&gt;10% of PHP pages use version 5!&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  PHP 6
&lt;/h3&gt;

&lt;p&gt;Legendary 🦄 sixth version of PHP. How to summarise it? I think this comic does it pretty well:&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://nophpunintended.com/the-unifant/" rel="noopener noreferrer"&gt;&lt;em&gt;No PHPun Intended comic about PHP 6&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And now being more serious - why PHP 6 was never released? The plan was to introduce deep native support for Unicode in PHP. Unfortunately, the number of technical difficulties around this implementation (including performance issues and backward compatibility) led to abandoning the PHP 6 project in 2010 and its Unicode features. Many of the non-Unicode features that had been developed for PHP 6 were incorporated into PHP 5.3 and 5.4 instead. For the next PHP version web developers had to wait for over a decade. But in the meantime, two important things happen to PHP. First of all, in 2009 we founded &lt;a href="https://accesto.com/" rel="noopener noreferrer"&gt;Accesto&lt;/a&gt; 😉 And a year later, Taylor Otwell released the very first version of Laravel. For some reason I see the first one as a more important fact - but I guess you may disagree 😉&lt;/p&gt;

&lt;h3&gt;
  
  
  PHP 7
&lt;/h3&gt;

&lt;p&gt;This version feels like yesterday, but is already 10 years old! Released in 2015, delivered substantial performance enhancements (up to twice as fast as PHP 5.6 🚀) and reduced memory consumption. With its subsequent versions 7.1-7.4 added a lot of language features like the ?? operator, anonymous classes and return type declaration, just to name some. &lt;/p&gt;

&lt;h3&gt;
  
  
  PHP8
&lt;/h3&gt;

&lt;p&gt;PHP 8.0 was released in 2020 with many new features (eg. named arguments, union types, attributes, null safe operator) and significant performance improvements (huge difference thanks to the JIT compiler). Further minor releases brought us enumerations, fibers, readonly classes, DNF Types, and typed class constants. And many more! An impressive list for just a minor release I must say. &lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;The last PHP version (8.4) introduced property hooks, asymmetric visibility and array functions&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  PHP.... The Foundation
&lt;/h3&gt;

&lt;p&gt;November 2021 sets another important milestone in PHP history. Nikita Popov, one of the significant contributors to PHP is leaving the project. Is it the end of PHP? On the contrary! This event became a catalyst for a series of events which in the end led to &lt;a href="https://accesto.com/blog/php-foundation/" rel="noopener noreferrer"&gt;the PHP Foundation being established&lt;/a&gt;. With its mission to ensure the long-term prosperity of the PHP language. From now on, PHP as a programming language has a solid team of core developers, contributors and supporters. &lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;The goal of The PHP Foundation is to ensure the long-term prosperity of the PHP language&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we can learn from PHP history for today?
&lt;/h2&gt;

&lt;p&gt;The history of PHP shows that this was never a hype-driven language. Throughout its history, PHP wasn't the first one to introduce various features. It adopted object-oriented programming pretty late and for many years was pretty far from types.  But at the same time, it never stopped evolving! PHP may not be up to date with what's latest or sexy and may not have all the sugar yet. But it is getting the modern syntax eventually. May not be the first one, but thanks to that, it does implement only those solutions, that proved to be useful in other languages and other technologies. And eventually, modern PHP code does not differ much from any other contemporary language used by web developers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting mature, not old!
&lt;/h3&gt;

&lt;p&gt;For the last 30 years, PHP has constantly developed and improved as a language. It wasn't getting old through these years! On the contrary, it was constantly evolving and adapting to modern web development. &lt;/p&gt;

&lt;p&gt;How much more you can improve after 30 years of constant development? PHP shows that quite a lot. &lt;a href="https://accesto.com/blog/php-8-4/" rel="noopener noreferrer"&gt;The last version 8.4&lt;/a&gt; introduced property hooks, asymmetric visibility and array functions. But one thing is language features, another is better security and performance improvements.&lt;/p&gt;

&lt;p&gt;And speaking of performance. Last month we upgraded PHP for one of our clients from 8.1 to the latest 8.4. Results? Approximately 15-20% performance boost:&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Web transaction time was shortened by 15-20% after upgrading&lt;/em&gt; &lt;strong&gt;&lt;em&gt;PHP to the latest 8.4 version&lt;/em&gt;&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;We have already seen some good performance boosts in this project with previous upgrades within version 7 and version 8. So I wasn't really expecting any significant further improvements this time. But PHP continues to surprise me. After 30 years we can still expect some nice gains. Good job to all the core PHP developers! 💪&lt;/p&gt;

&lt;h3&gt;
  
  
  PHP was built out of necessity
&lt;/h3&gt;

&lt;p&gt;Personally, I also like the fact that PHP was created by Rasmus Lerdorf to solve his real-life problem. And for years it continued to evolve in that very manner — driven by real needs, not by hype. Also the same was with the PHP Foundation. It wasn't established to gain power over PHP development or because having a foundation is cool. It was built out of a real need to support PHP and its growth. &lt;/p&gt;

&lt;p&gt;Recently our CTO did a small &lt;a href="https://accesto.com/blog/php-foundation-interview/" rel="noopener noreferrer"&gt;interview with Roman Pronskiy&lt;/a&gt;, an Executive director of the PHP Foundation. Roman revealed couple of initiatives that will shape the future of PHP in the upcoming months and years. And of them are driven by real needs, not by hype. Examples? Introduction of generic types in PHP, and extensive security audits for PHP language, supported by German government-backed agency!&lt;/p&gt;

&lt;h2&gt;
  
  
  Happy birthday, PHP!
&lt;/h2&gt;

&lt;p&gt;Some may think that a programming language that is 30 years old is a finished history. That after so many years we should not expect any new language features. But that's definitely not the case with the PHP! It may not be the first one that introduces various features, but you don't have to be first to be solid, reliable, and thanks to that... relevant. Personally, I like PHP because after 30 years it can still get better and better.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Creating Angular Tooltip Directive - Part 2: adding customisation</title>
      <dc:creator>Piotr Gołofit</dc:creator>
      <pubDate>Mon, 17 Oct 2022 13:54:15 +0000</pubDate>
      <link>https://dev.to/accesto/creating-angular-tooltip-directive-part-2-adding-customisation-4o6l</link>
      <guid>https://dev.to/accesto/creating-angular-tooltip-directive-part-2-adding-customisation-4o6l</guid>
      <description>&lt;p&gt;In my &lt;a href="https://accesto.com/blog/how-to-create-angular-tooltip-directive/"&gt;previous blog post&lt;/a&gt;, I explained how to create your own angular tooltip directive without using any third-party component libraries (like using angular material tooltip). Today, I will show you how to add more customisation to build a fully-fledged tooltip module.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: the whole code from this tutorial is on &lt;a href="https://github.com/accesto/angular-tooltip-directive" rel="nofollow"&gt;GitHub&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Customising Angular tooltips
&lt;/h2&gt;

&lt;p&gt;The tooltip directive created in &lt;a href="https://accesto.com/blog/how-to-create-angular-tooltip-directive/"&gt;tutorial part 1&lt;/a&gt;. should be good enough for many basic use cases. But in more complex situations, you may be tempted to install some dependencies to external angular tooltip libraries. Let's avoid this, and instead, let's take our own tooltip directive to the next level. Today, we will make it way more universal by adding a few features &amp;amp; options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tooltip position (below, above, left or right);&lt;/li&gt;
&lt;li&gt;tooltip themes (dark theme vs. light theme);&lt;/li&gt;
&lt;li&gt;tooltip delay time (separate tooltip delay show &amp;amp; tooltip delay hide);&lt;/li&gt;
&lt;li&gt;dynamic tooltip that follows the mouse cursor;&lt;/li&gt;
&lt;li&gt;support for touch devices / mobile devices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without further ado, let's jump straight into the first customisation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tooltip position - angular tooltips in the right place
&lt;/h2&gt;

&lt;p&gt;So far, our tooltip was always displayed below the parent element. Now we will add an additional attribute position that will allow us to display it more flexibly. We will allow it to be placed below, above, left or right of the host element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;TooltipPosition&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ABOVE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;above&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;BELOW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;below&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;LEFT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;RIGHT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;right&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;DEFAULT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;above&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To keep our code nice and clean, let's create tooltip.enums.ts file, where we will define enum types for all the available customization properties. In addition to apparent options for TooltipPosition (above/below/left/right), you can also define a default position in our enum.&lt;/p&gt;

&lt;p&gt;Now, let's update the ts file of our directive to accept the position attribute as an @Input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;TooltipDirective&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;tooltip&lt;/span&gt; &lt;span class="o"&gt;=&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="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TooltipPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TooltipPosition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DEFAULT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;a href="https://accesto.com/blog/how-to-create-angular-tooltip-directive/"&gt;part 1&lt;/a&gt; of this tutorial we created a method setTooltipComponentProperties(). In that method, we calculated the position of our tooltip relative to the HTML element on which the user hovers (to display it right below it). New version of this method will have separate calculations for each of the TooltipPosition options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;setTooltipComponentProperties&lt;/span&gt;&lt;span class="p"&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tooltip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;elementRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getBoundingClientRect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;TooltipPosition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BELOW&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;TooltipPosition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ABOVE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;TooltipPosition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RIGHT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bottom&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;TooltipPosition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LEFT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bottom&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will also define the position property in the .ts file of our TooltipComponent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;TooltipComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TooltipPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TooltipPosition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DEFAULT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The value of the position property is assigned in the setTooltipComponentProperties in the code above. We will use it to set a proper modifier to our CSS tooltip class...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="tooltip"
     [ngClass]="['tooltip--'+position]"
     [style.left]="left + 'px'" [style.top]="top + 'px'"&amp;gt;
  {{tooltip}}
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...to properly align our tooltip in the desired position:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.tooltip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nc"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;--below&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nt"&gt;transform&lt;/span&gt;&lt;span class="nd"&gt;:translateX&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-50&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;7px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;--above&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nt"&gt;transform&lt;/span&gt;&lt;span class="nd"&gt;:translate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-50&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;-100&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;7px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;--left&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nt"&gt;transform&lt;/span&gt;&lt;span class="nd"&gt;:translate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;calc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-100&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt; &lt;span class="nt"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;7px&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="nt"&gt;-50&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="o"&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;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;--right&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nt"&gt;transform&lt;/span&gt;&lt;span class="nd"&gt;:translateY&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-50&lt;/span&gt;&lt;span class="nv"&gt;%&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;7px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am using a &lt;a href="https://getbem.com/" rel="nofollow"&gt;BEM&lt;/a&gt; notation, so it will be eg. tooltip--below or tooltip--left, but feel free to adjust it to the CSS conventions of your project.&lt;/p&gt;

&lt;p&gt;Also, if you added a small triangle indicating the tooltip anchor point, you should also adjust it for new positions. Below is just an example of the right-positioned tooltip, feel free to check the repo for the full CSS here: &lt;a href="https://github.com/accesto/angular-tooltip-directive" rel="nofollow"&gt;&lt;/a&gt;&lt;a href="https://github.com/accesto/angular-tooltip-directive"&gt;https://github.com/accesto/angular-tooltip-directive&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.tooltip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="err"&gt;...
    &amp;amp;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="nc"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;--right&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;...
    &amp;amp;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;border-top-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;border-bottom-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;border-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;50%&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that is it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;div class="cat-icon" 
    [tooltip]="'Meow on the right!'"
    position="right"&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: if your project setup (in tsconfig.json) requires angularCompilerOptions.strictTemplates you may need to import the enum type and use [position]=TooltipPosition.RIGHT instead of simple position="right".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XVRfegyN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/szqwy0175dva28vo6uxg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XVRfegyN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/szqwy0175dva28vo6uxg.png" alt="How to position tooltip" width="880" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tooltip themes - angular tooltips that match your UI
&lt;/h2&gt;

&lt;p&gt;Our next customisation will allow us to use our tooltip in both light and dark UIs.&lt;/p&gt;

&lt;p&gt;Again, we will start by defining an enum type for our TooltipTheme options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;TooltipTheme&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;DARK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;LIGHT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;DEFAULT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on the theme, we will adjust the tooltip background, and of course the tooltip text colour. We also have to adjust the tooltip anchor point (small triangle) to match the tooltip background:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.tooltip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nc"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;--light&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;white&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="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;--dark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;black&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="nc"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to make use of these themes, we have to pass the theme property to our component template the same way as we did with the position. I will skip that part here but feel free to check the complete code in the repo: &lt;a href="https://github.com/accesto/angular-tooltip-directive" rel="nofollow"&gt;&lt;/a&gt;&lt;a href="https://github.com/accesto/angular-tooltip-directive"&gt;https://github.com/accesto/angular-tooltip-directive&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rJUx8HXj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gsoq6zkqemtrksapknai.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rJUx8HXj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gsoq6zkqemtrksapknai.png" alt="Tooltip theme customisation" width="796" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tooltip delay time - instead of showing the tooltip immediately
&lt;/h2&gt;

&lt;p&gt;Now, let's play a little bit with the tooltip behaviour. So far we just wanted to show the tooltip immediately when the user's mouse hovers over the selected HTML elements. But common practice is to show the tooltip after the user hovers over the tooltip's trigger element for a longer period (measured in milliseconds) - which may indicate that he needs some help or explanation. Let's add some delay time before we make our tooltip visible. Options are two - we can either instantiate our tooltip component after the delay, or do it immediately, but play with its opacity. I'll go with the second option, as it will give more options to control the potential show animations/transitions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.tooltip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="nc"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;--visible&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt; &lt;span class="m"&gt;300ms&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To control visibility, we have to define visible: boolean = false property in our TooltipComponent and use it in the class directive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[class.tooltip--visible]="visible"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's now add setTimeout() to our onMouseEnter() method of our tooltip directive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt; &lt;span class="nx"&gt;onMouseEnter&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setTooltipComponentProperties&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;showTimeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;showTooltip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;showDelay&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="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;showTooltip&lt;/span&gt;&lt;span class="p"&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visible&lt;/span&gt; &lt;span class="o"&gt;=&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where this.showDelay (defined in milliseconds) will of course be another @Input() property of our angular tooltip directive. For formality, let's also clear the timeout if the user's mouse leaves the tooltip's trigger element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;showTimeout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same we can do with the hide delay. For that, we need to modify onMouseLeave() method of our angular tooltip directive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HostListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mouseleave&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;onMouseLeave&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hideTimeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hideDelay&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, hideDelay will be an @Input() property that will define closing time in milliseconds. And of course, we also have to clear the timeout of our close delay in case the user hovers again over the parent element.&lt;/p&gt;

&lt;p&gt;And that is it - see an angular tooltip example in action, with delays for showing and hiding the tooltip:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ip01G8Jt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/on6u4x4ok4f7jo7xamo1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ip01G8Jt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/on6u4x4ok4f7jo7xamo1.gif" alt="Tooltip delay customisation" width="794" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You could also add some hiding animation/transition after the hide delay, but I will leave that to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamic tooltips - eg. when the user hovers over an area
&lt;/h2&gt;

&lt;p&gt;Tooltips, as the name suggests, are often used to provide additional information on tools that users have in the UI. Let's assume you are building an online graphics editor. It's a common case, to show the mouse coordinates over the editor canvas. For that, we will need a tooltip that won't appear in one place, but will rather follow the user's mouse. For that, we will let our angular tooltips listen to pointer events, and update their position whenever the user moves the cursor. Let's call it a dynamic tooltip position:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;TooltipPosition&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ABOVE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;above&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="nx"&gt;DYNAMIC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dynamic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;DEFAULT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;above&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We could also define the tooltip position left/right/below/above the mouse cursor, but let's not overcomplicate this and stick to just the default behaviour. We will simply display the tooltip next to the cursor, and will hide the anchor point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;--dynamic&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Listening to the pointer events is quite straightforward, we just need to add another @HostListener that will update the position of our angular tooltip whenever the user moves the mouse:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HostListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mousemove&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="nx"&gt;onMouseMove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MouseEvent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;TooltipPosition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DYNAMIC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tooltip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may be wondering why we also update the this.componentRef.instance.tooltip? It's because otherwise, we won't be able to dynamically update the tooltip content. This allows us to achieve tooltips like these:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--62od4MOt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yjs84clyy4klpmwmra2t.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--62od4MOt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yjs84clyy4klpmwmra2t.gif" alt="Tooltip dynamic customisation" width="502" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Support for touch devices - mobile tooltips
&lt;/h2&gt;

&lt;p&gt;Last but not least, we will focus on making our tooltip module compatible with mobile devices. What's different on mobile? Lack of the mouse pointer of course! So how we can show our angular tooltip if the user cannot hover his mouse over anything? It's quite simple actually. We just need to adjust our tooltip directive and in addition to pointer events, listen also to touch gestures.&lt;/p&gt;

&lt;p&gt;First, let's determine what kind of gestures we want to detect. Shall we show the tooltip whenever the user taps (touches) some button or other HTML content? That would be quite straightforward to implement, but would actually prevent users from using that button. We still have to allow all the standard taps for the normal usage of the UI. Instead, let's detect the situation when the user long presses the screen over the HTML content. But how do we define the long press?&lt;/p&gt;

&lt;p&gt;Unfortunately, there is no touch event that would express the long press. But no worries, we can combine touchstart and touchend events, to detect if it was indeed a long press.&lt;/p&gt;

&lt;p&gt;So far we have been initialising our tooltip right inside the onMouseEnter method. Now, because we will have more than one trigger than the mouse, let's move the initialisation to a separate method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HostListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mouseenter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;onMouseEnter&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initializeTooltip&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;initializeTooltip&lt;/span&gt;&lt;span class="p"&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we will use that method also after we detect the long press:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HostListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;touchstart&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="nx"&gt;onTouchStart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TouchEvent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;touchTimeout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;touchTimeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initializeTooltip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HostListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;touchend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;onTouchEnd&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;touchTimeout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setHideTooltipTimeout&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;setHideTooltipTimeout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hideTimeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hideDelay&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple as that, now our angular tooltips are ready for mobile devices. Cheers! 🍺&lt;/p&gt;

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

&lt;p&gt;In &lt;a href="https://accesto.com/blog/how-to-create-angular-tooltip-directive/"&gt;part 1 of this tutorial&lt;/a&gt;, we created a custom angular tooltip directive for displaying very basic tooltips. Part 2. brought many customisations to our tooltip module. You should now be able to use it in most of your angular applications. Sure it still has fewer features than the best angular tooltip libraries (like an angular material tooltip), but if you miss some features, feel free to extend it further.&lt;/p&gt;

&lt;p&gt;You can play with the default options &amp;amp; default values like default delay. You can add further settings like font size or passing custom css class. Or you can even consider creating an angular tooltip service, for non-manual triggers (to show/hide the tooltip programmatically).&lt;/p&gt;

&lt;p&gt;I will leave it to you. And in the meantime, you can check the complete code of the angular tooltip directive from this tutorial here: &lt;a href="https://github.com/accesto/angular-tooltip-directive" rel="nofollow"&gt;&lt;/a&gt;&lt;a href="https://github.com/accesto/angular-tooltip-directive"&gt;https://github.com/accesto/angular-tooltip-directive&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>How to create your own Angular Tooltip Directive - a short tutorial</title>
      <dc:creator>Piotr Gołofit</dc:creator>
      <pubDate>Tue, 09 Aug 2022 14:45:00 +0000</pubDate>
      <link>https://dev.to/accesto/how-to-create-your-own-angular-tooltip-directive-a-short-tutorial-k3i</link>
      <guid>https://dev.to/accesto/how-to-create-your-own-angular-tooltip-directive-a-short-tutorial-k3i</guid>
      <description>&lt;p&gt;Tooltips may seem insignificant, shy, and sometimes neglected. But they are real superheroes of many user interfaces. And like superheroes, they appear from nowhere, right when you need them, exactly where the help is needed. But, what is their superpower?&lt;/p&gt;

&lt;p&gt;Tooltips are really powerful at delivering information. They are a perfect way to provide contextual help, without overloading users with too much information at the first glance. Tooltips appear wherever users need some additional data or explanation. No sooner, no later. This allows you to maintain the UI nice, clean and minimalistic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E9UpNFb2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z0ffomtq1hgc2n3h5uqv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E9UpNFb2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z0ffomtq1hgc2n3h5uqv.png" alt="Tooltips are commonly used as a pop up tip to explain the meaning of various tools, buttons and icons" width="880" height="256"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Tooltips are commonly used as a pop up tip to explain the meaning of various tools, buttons and icons&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Providing help in form fields, explaining complex features or tools, displaying additional information over charts... There are many ways tooltips can improve the user experience of your application. So if you are a front-end developer building an app in Angular, there is a huge chance that sooner or later you will be adding tooltips here and there.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to add tooltips to an Angular app?
&lt;/h2&gt;

&lt;p&gt;Before we dive into creating our own Angular Tooltip Directive, let's see some other ways to add tooltips in the Angular app. One of the most popular ways is to use the Angular &lt;a href="https://material.angular.io/components/tooltip/overview"&gt; Material &lt;/a&gt; component library. It provides a large set of components that follow the Google Material Design system. Another common UI library that you can consider is &lt;a href="https://ng-bootstrap.github.io/#/components/tooltip"&gt; ng-bootstrap &lt;/a&gt;- it is an Angular version of the previously most popular HTML/CSS/JS library in the world - Bootstrap. If you seek something more lightweight yet powerful, you can also check &lt;a href="https://www.npmjs.com/package/ng2-tooltip-directive"&gt; ng2 tooltip directive&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OSxj7r1w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/prn5yj43ct2uubefj2pa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OSxj7r1w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/prn5yj43ct2uubefj2pa.png" alt="Examples of Angular Material &amp;amp; NG Bootstrap tooltips" width="880" height="205"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Examples of Angular Material &amp;amp; NG Bootstrap tooltips&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  So why should I create my own Tooltip directive in Angular?
&lt;/h2&gt;

&lt;p&gt;With so many tooltip solutions available, creating your own tooltip directive may seem like reinventing the wheel. But here are some reasons why you still may want to consider building it from the scratch on your own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Because you don't want to rely on yet another npm package or you just want to keep your app lightweight - without adding the whole Angular material to achieve tooltips;&lt;/li&gt;
&lt;li&gt;Because you may need more customisation to your tooltip style (like light theme vs dark theme) or behaviour (eg. dynamic tooltips that follow your mouse - I will cover that in part 2. of this tutorial). You may also want to add some directive options like content type, custom hover options (animation, hide delay, show delay) or a support for touch devices / mobile devices;&lt;/li&gt;
&lt;li&gt;Because you may be building a whole component framework for your app and want tooltips to be part of it;&lt;/li&gt;
&lt;li&gt;Last but not least - because you may want to learn how to build angular structural directives, and although there may be some simpler directives to start with, tooltips will give you a good lesson.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if you already decided to build your own tooltips, let's see how we can approach that task and what building blocks of Angular shall we use.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tooltip as a Directive, Component or as Service?
&lt;/h2&gt;

&lt;p&gt;It depends, of course 😉 The same way as a custom button in Angular can be implemented as a standalone component tag, or as a directive applied on a standard HTML tag. Today I will show you how to implement a tooltip in Angular as a directive because I find it the most common use case. Usually, tooltips don't live on their own (like Components) and don't need to be invoked programmatically (via Services). They are usually add-ons to existing HTML content, and that is where &lt;a href="https://angular.io/guide/structural-directives"&gt;structural directives&lt;/a&gt; work best.&lt;/p&gt;

&lt;p&gt;But as your use case may be different, you may consider implementing tooltips as an:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Angular Directive - plain and simple, just an add-on to existing HTML code, usually containing just a string or a short text;&lt;/li&gt;
&lt;li&gt;Angular Component - when you need to place inside a tooltip something more sophisticated than a text or an image, eg. some additional data above an interactive chart, or even to make universal tooltips that can display any given content inside the ng template tag;&lt;/li&gt;
&lt;li&gt;Angular Service - mostly if you need to programmatically add or manipulate tooltips from the TypeScript code, eg. from within the functions of other components or services.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ULFYVTR1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yrz31lrqzh6h0ai7frq8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ULFYVTR1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yrz31lrqzh6h0ai7frq8.png" alt="Tooltips above charts - potential place where you can consider implementing tooltips as a components" width="880" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Tooltips above charts - potential place where you can consider implementing tooltips as a components&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating Tooltip Directive in Angular - step-by-step recipe
&lt;/h2&gt;

&lt;p&gt;Ok, so let's start from the short &lt;strong&gt;todo list&lt;/strong&gt; with the steps required to build our reusable tooltips:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a tooltip component with a template;&lt;/li&gt;
&lt;li&gt;Define tooltip background and other styles;&lt;/li&gt;
&lt;li&gt;Create a tooltip directive;&lt;/li&gt;
&lt;li&gt;Add basic tooltip logic;&lt;/li&gt;
&lt;li&gt;Wrap it up in the tooltip module;&lt;/li&gt;
&lt;li&gt;Give it a test ride!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And here is a list of necessary ingredients for our Angular tooltip directive recipe:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 component - to define a template and a style of our tooltip;&lt;/li&gt;
&lt;li&gt;1 directive - to attach tooltips to HTML elements;&lt;/li&gt;
&lt;li&gt;1 module - to wrap it all up and make it reusable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Component, directive and a module?! You mentioned only a directive before! Yup, you got me! I did cheat on you, but just a little. You can actually achieve all of that with just an Angular directive. But having a component inside a directive (kind of) gives us a more convenient way for styling, and a module will help us to wrap it up and keep our code clean. So apologies, but I had good intentions 🙈&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: you will find the whole code from this tutorial in our repository: &lt;a href="https://github.com/accesto/angular-tooltip-directive"&gt;&lt;/a&gt;&lt;a href="https://github.com/accesto/angular-tooltip-directive"&gt;https://github.com/accesto/angular-tooltip-directive&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Step 0 - set up an Angular project or use your existing one
&lt;/h2&gt;

&lt;p&gt;For those of you willing to reuse your existing project, just skip to step 1. but if you want to start from scratch, please open your favourite IDE, and in the terminal type in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;ng&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;angular&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;tooltips&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;tutorial&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And follow the standard installation process of a fresh Angular project. After the project is up and running, replace the contents of app.component.html with a single button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Do&lt;/span&gt; &lt;span class="nx"&gt;something&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, with a &lt;a href="https://github.com/accesto/angular-tooltip-directive/blob/master/src/assets/cat-icon.svg"&gt;cat icon&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 - create a tooltip component
&lt;/h2&gt;

&lt;p&gt;Let's start by generating a tooltip component via Angular CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;ng&lt;/span&gt; &lt;span class="nx"&gt;generate&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="nx"&gt;common&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;tooltip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I like to keep all the common UI building blocks (like buttons, tooltips, labels etc.) in app/common/ui/directory, but of course, feel free to adjust to your preferences.&lt;br&gt;
Our tooltip component will actually be just a nice wrapper for a template and some CSS, no fancy logic required, so tooltip.component.ts can be as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&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="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tooltip&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./tooltip.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./tooltip.component.scss&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;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;TooltipComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nl"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nx"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Later we will add some customisation, but for now, the only difference from an empty component boilerplate are three properties - tooltip, left and top. These will keep a tooltip text and position (tooltips will be placed globally within the browser window and we will be using position:fixed for locating them).&lt;/p&gt;

&lt;p&gt;In the template, we just need to display tooltip text and attach its location to the left and top properties of a component, so HTML content of tooltip.component.html would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tooltip&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
     &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;left + 'px'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;top + 'px'&lt;/span&gt;&lt;span class="dl"&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;tooltip&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple enough, isn't it? Let's now add some basic styles to make template content look like an actual tooltip.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 - define tooltip background and other styles
&lt;/h2&gt;

&lt;p&gt;Here is an exemplar styling with a black background, white font and some rounded corners, but feel free to make it your own:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.tooltip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;13px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;translateX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-50%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As already mentioned, tooltips will work globally, thus position: fixed. Last line with transform:translateX(-50%) will ensure that tooltip self-positions itself horizontally.&lt;br&gt;
You may also want to define some max width or z-index, but I will leave that with you. Right now, let's maybe just add a nice triangle indicating an anchor point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.tooltip&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;(...)&lt;/span&gt;
  &lt;span class="err"&gt;&amp;amp;::before&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;50%&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the overall tooltip will look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I6pwLhHr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0zgyhzprqe0aj6h25rt1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I6pwLhHr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0zgyhzprqe0aj6h25rt1.png" alt="Tooltip style" width="638" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As for now, we aim to display the tooltip below the HTML element, but in part 2. of this tutorial I will explain how to add some additional usage options - including tooltip location/direction, show delay or closing time. But first, let's finally create an Angular Tooltip Directive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 - create a tooltip directive
&lt;/h2&gt;

&lt;p&gt;Directives can be created via Angular CLI in the same way as components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;ng&lt;/span&gt; &lt;span class="nx"&gt;generate&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="nx"&gt;common&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;tooltip&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;tooltip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mind the additional /tooltip in the path, as we want our directive to be generated in the already existing folder with a component.&lt;/p&gt;

&lt;p&gt;Now, let's declare, inject and import a few objects that we will need inside our directive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;TooltipComponent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./tooltip.component&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="nd"&gt;Directive&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[tooltip]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;TooltipDirective&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;tooltip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;elementRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ElementRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;appRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApplicationRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;componentFactoryResolver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentFactoryResolver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Injector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We start by importing our tooltip component class, as we will create its instance (via component factory) every time we want to show a pop-up tip to your users. We also need a few objects to be injected into our constructor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;elementRef - we will use it to access the HTML element on which we place the directive tag, eg. a button or a link, to check its position in the browser window;&lt;/li&gt;
&lt;li&gt;appRef - reference to a whole app, that will allow us to inject our tooltip to the application HTML content globally;&lt;/li&gt;
&lt;li&gt;componentFactoryResolver - that will provide us with a factory for creating our TooltipComponent programmatically inside the TooltipDirective code;&lt;/li&gt;
&lt;li&gt;injector - that will provide &lt;a href="https://angular.io/guide/dependency-injection"&gt;Angular dependency injection&lt;/a&gt; for our TooltipComponent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, we declare a componentRef property, to store the reference to a TooltipComponent, which we will be instantiating via the factory.&lt;/p&gt;

&lt;p&gt;Remember also to provide a meaningful selector name for the directive. I just used a [tooltip] for convenience, but you can name it eg. [appTooltip].&lt;/p&gt;

&lt;p&gt;Then, let's define an attribute that we will use to pass as an @Input() to our tooltip. Right now we just need a tooltip content, so you can name it simply a tooltip or something like tooltipText.&lt;/p&gt;

&lt;p&gt;Again, for convenience, I stuck with the tooltip as it allows me to simplify the syntax, and use the directive easier. So instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&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;appTooltip&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can skip the selector part and simply write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is of course, convenient, but may sometimes cause you trouble if a tooltip property conflicts with some other directives you may have, so watch out!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 - add basic tooltip logic
&lt;/h2&gt;

&lt;p&gt;After having all the groundwork done, let's implement some tooltip logic. We will start by creating an actual tooltip on hover, or more precisely onMouseEnter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HostListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mouseenter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;onMouseEnter&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;componentFactory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
              &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentFactoryResolver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolveComponentFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nx"&gt;TooltipComponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;componentFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attachView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostView&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;domElem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
              &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostView&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;EmbeddedViewRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;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;rootNodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;domElem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setTooltipComponentProperties&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method will be listening (thanks to @HostListener) to mouse pointer events relative to the DOM element, on which we have applied our tooltip directive. Event mouseenter is emitted each time the user's cursor starts to hover above our DOM element. When that happens, we first check if our tooltip does not exist yet (using its reference this.componentRef) and if not, we will move on with its creation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First of all, we need to use the resolveComponentFactory to get the factory that can manufacture our tooltip component;&lt;/li&gt;
&lt;li&gt;Then, we use it to create the component, passing the dependency injector;&lt;/li&gt;
&lt;li&gt;Next, we attach our component to the virtual DOM of the whole app, via the appRef;&lt;/li&gt;
&lt;li&gt;We also attach it to the real DOM via a standard document.body.appendChild;&lt;/li&gt;
&lt;li&gt;Last but not least, we define and invoke methods to set up the position and title of our tooltip.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This method for setting up the properties would like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;setTooltipComponentProperties&lt;/span&gt;&lt;span class="p"&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tooltip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;           
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;elementRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getBoundingClientRect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Method description is quite straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access to this.componentRef.instance allows the use of an actual instance of our component so we can set up its properties like tooltip text (I did access component properties directly, but I encourage you to define some setters instead);&lt;/li&gt;
&lt;li&gt;getBoundingClientRect() - allows us to get the HTML element location and based on that calculate the tooltip coordinates relative to a browser window.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is how we handle hover, now it's time to take care of tooltip hiding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;HostListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mouseleave&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;onMouseLeave&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;ngOnDestroy&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detachView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostView&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two situations in which we have to make sure that our tooltip hides: when we hover out (mouseleave event), and when the element on which we applied our angular tooltip directive is destroyed (ngOnDestroy).&lt;/p&gt;

&lt;p&gt;And... that's it! Let's only wrap it up and we are good to go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 - wrap it up in a tooltip module
&lt;/h2&gt;

&lt;p&gt;Again, we will use the Angular CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;ng&lt;/span&gt; &lt;span class="nx"&gt;generate&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="nx"&gt;common&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;tooltip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And inside the module, we will declare our component and directive, and will export our final Angular tooltip directive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NgModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CommonModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;TooltipComponent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./tooltip.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;TooltipDirective&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./tooltip.directive&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="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;declarations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;TooltipComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;TooltipDirective&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;CommonModule&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;TooltipDirective&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;TooltipModule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And... done. Let's now give it a test ride.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6 - give it a test ride!
&lt;/h2&gt;

&lt;p&gt;To use tooltips we just have to add our TooltipModule in app.module.ts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;TooltipModule&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./common/ui/tooltip/tooltip.module&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="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;imports&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;TooltipModule&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we are ready to apply the directive like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cat-icon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'Meow!'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or for a button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'Some explanation'&amp;gt;Do something&amp;lt;/button&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that was it - you can now hover above your button (or a cat 😺) and see how our tooltip appears:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SL2XIfPj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gytrsvy0622ohosq002p.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SL2XIfPj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gytrsvy0622ohosq002p.gif" alt="Tooltip appearance" width="549" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How can we improve our Angular tooltips?
&lt;/h2&gt;

&lt;p&gt;That was just a very basic tutorial on building your own angular tooltip directives. Bear with me and wait for part 2. in which we will extend this with many useful customisation and usage options. Part 2. will include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tooltip hide delay / show delay;&lt;/li&gt;
&lt;li&gt;Tooltip position: above/below/left/right;&lt;/li&gt;
&lt;li&gt;Dynamic tooltips that follow the mouse cursor;&lt;/li&gt;
&lt;li&gt;Tooltip themes eg. dark vs tooltip background;&lt;/li&gt;
&lt;li&gt;Default options &amp;amp; default values like default delay;&lt;/li&gt;
&lt;li&gt;Tooltip showing and tooltip hiding animations;&lt;/li&gt;
&lt;li&gt;Support for touch devices / mobile devices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cheers! 🍺&lt;/p&gt;

</description>
      <category>angular</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Making sure your website won't crash again</title>
      <dc:creator>Piotr Gołofit</dc:creator>
      <pubDate>Fri, 17 Jun 2022 06:55:26 +0000</pubDate>
      <link>https://dev.to/accesto/making-sure-your-website-wont-crash-again-53ba</link>
      <guid>https://dev.to/accesto/making-sure-your-website-wont-crash-again-53ba</guid>
      <description>&lt;p&gt;In my &lt;a href="https://accesto.com/blog/why-new-users-can-kill-your-web-application/" title="Why new users can kill your web application?"&gt;previous blog post&lt;/a&gt;, I wrote about situations when an increase in user traffic could cause your website a lot of trouble. Unexpected popularity, instead of bringing you new customers, could cause the web services to go down. Today I would like to share with you a story from my own experience, a lesson learned from the &lt;b&gt;spectacular crash of a popular startup&lt;/b&gt;. How did it happen? How did it recover? And how this spectacular disaster hardened the website and made it ready for the future?&lt;/p&gt;

&lt;h2&gt;
  
  
  A successful startup gaining traction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://szukamlapka.pl/" title="SzukamLapka.pl"&gt;SzukamLapka&lt;/a&gt; is a very popular polish website that &lt;b&gt;advises on purchasing laptops&lt;/b&gt;. It helps both IT newbies, and more tech-savvy customers as well. Its AI-driven algorithms review specs, benchmarks and offers to "understand" the industry and generate &lt;b&gt;personalized rankings of the best gear&lt;/b&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Pq-TOFqo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zyrf92ei8z0u1ey0uq39.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Pq-TOFqo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zyrf92ei8z0u1ey0uq39.jpg" alt="NoteookRank homepage - advice on purchasing laptop for tech and non-tech peopl" width="880" height="527"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;center&gt;Advice on purchasing laptop for non-tech and more tech-savvy customers&lt;/center&gt;
&lt;br&gt;

&lt;p&gt;As &lt;a href="https://accesto.com/" title="Accesto.com"&gt;Accesto&lt;/a&gt;, we have been their partners since early, MVP stages of SzukamLapka. This year they are &lt;b&gt;launching a global version&lt;/b&gt; under the brand &lt;a href="https://notebookrank.com/" title="NotebookRank.com"&gt;NotebookRank&lt;/a&gt;, but a few years ago, they were just a local startup, &lt;b&gt;growing and gaining traction&lt;/b&gt; on a polish market. But in the late Monday evening of August 2017, this growth got out of control...&lt;/p&gt;

&lt;h2&gt;
  
  
  Surviving the hug of death
&lt;/h2&gt;

&lt;p&gt;Almost every country has its equivalent to Reddit or Digg websites - places where users share, rate, and discuss &lt;b&gt;useful links&lt;/b&gt;. In Poland, it is &lt;a href="https://wykop.pl/" title="Wykop.pl" rel="nofollow"&gt;Wykop.pl&lt;/a&gt;, and on that day, someone who considered SzukamLapka to be very useful shared this knowledge with Wykop users. And it wasn’t the only person who found the named service worth using...&lt;/p&gt;

&lt;p&gt;In the first hour after being mentioned the &lt;b&gt;number of visitors doubled&lt;/b&gt;, but that was just a beginning. The exponential growth continued for the next 5 hours, reaching almost 10,000% late evening. That is a &lt;b&gt;hundred times more users than usual!&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sHo6F7Tb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gjiv6xpi2790dkwitnny.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sHo6F7Tb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gjiv6xpi2790dkwitnny.jpg" alt="Google Analytics: 1,000% increase in day-to-day traffic, and 10,000% in the peak!" width="812" height="413"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;center&gt;Google Analytics: 1,000% increase in day-to-dday traffic, and 10,000% in the peak!&lt;/center&gt;
&lt;br&gt;

&lt;p&gt;Usually, it is called a Reddit Hug of Death, or a Slashdot Effect, and in Poland... a Wykop Effect. It derives from the names of popular websites that caused many other sites to &lt;b&gt;crash due to the abrupt increase in short term traffic&lt;/b&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what happened?
&lt;/h2&gt;

&lt;p&gt;Probably you won't be surprised if I tell you that the website crashed. But, to be honest, we were a little bit surprised back then. The website was &lt;b&gt;already prepared to handle quite high volumes of traffic&lt;/b&gt;. It was optimized and performed well. Even when we did load testing with 20× more users than on an average day, all the rankings were generated in under 1 second. But sometimes you &lt;b&gt;can't spot the bottlenecks until you see them stuck&lt;/b&gt;.&lt;/p&gt;

&lt;p&gt;So what may be the bottleneck of a website that displays laptop rankings? The thing is that it isn’t just a simple website. Like most of the modern digital products, it is a complex web application, a multi-layer platform that consists of many services. And the bottleneck emerged between two of such services. The one that does all the complex calculations, and the other that uses these bare technical data and makes them more human-friendly. The question arose, why even though these two were connected via a robust and lightspeed API, the problem occurred?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k9uR5tVP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o5ciwb9w1d7psnqc4f1e.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k9uR5tVP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o5ciwb9w1d7psnqc4f1e.jpg" alt="504 error seen when site went down" width="880" height="544"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both services were performing very well, but sometimes the &lt;b&gt;connection between them was lost for a few short milliseconds&lt;/b&gt;. And that was the part that we did not take into account. Although this was a very short break in communication, one of the services, instead of retrying to connect, &lt;b&gt;kept hanging for around 3 minutes&lt;/b&gt;. We didn’t notice it in advance as it happened only once every few thousand page views. And of course, we had a few services like this working in parallel. So if one user waited too long, he could easily refresh the page and get the results immediately from another non-hanging service.&lt;/p&gt;

&lt;p&gt;But on that day, the volume of users was so high that this quite rare situation of stuck service happened on all the services, and users started to get 504 errors - indicating that the server did not respond in time. &lt;b&gt;The site went completely down&lt;/b&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solving the problem
&lt;/h2&gt;

&lt;p&gt;The temporary solution was quite simple. When we diagnosed the problem, we quickly &lt;b&gt;increased the number of available services and restarted all the stuck ones&lt;/b&gt;. We had to repeat this few times, as new services kept hanging. But in general, that temporarily solved the problem and allowed us to survive till the morning.&lt;/p&gt;

&lt;p&gt;The next day, we started by making sure that last night's nightmare won't happen again. We did it in 4 steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: adopt the fail-safe approach in your web application
&lt;/h3&gt;

&lt;p&gt;We already knew that there could be temporary connection issues with the API, and to handle this, we &lt;b&gt;added proper timeouts and retries&lt;/b&gt;. So if the connection is lost, service does not wait long, and after a second or so, tries to connect again. And there are few attempts, each with slightly longer timeouts (to get the response even if the network connection is slow for some reason).&lt;/p&gt;

&lt;p&gt;According to Murphy's law, if something can fail, it will fail, and it is true also for &lt;b&gt;web applications&lt;/b&gt;. It is good practice to take potential failures into account and make web apps ready for them by design. This is known as a &lt;b&gt;fail-safe approach&lt;/b&gt; and is especially important if you integrate with some external, third party API’s - which may not always be available, or behave in the way we expect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: make sure you calculate same things only once
&lt;/h3&gt;

&lt;p&gt;Although the connection with the API should no longer cause the trouble, we knew that this was the most compute-heavy part of the platform. So the fewer requests are sent to that API, &lt;b&gt;the better the website performance&lt;/b&gt; would be. We used &lt;a href="https://blackfire.io" title="blackfire.io" rel="nofollow"&gt;Blackfire.io&lt;/a&gt; to check how often the web app uses the API and we got interesting results. We noticed that the homepage, the most frequently visited page, used the API twice per each view. Why would a rather static page even require any API calls?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ItpA6KKt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e8fv0tw43weebmjz15lr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ItpA6KKt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e8fv0tw43weebmjz15lr.jpg" alt="Ranking in a header of SzukamLapka website" width="880" height="312"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;center&gt;Ranking sneak peak in header was calculated on every page view&lt;/center&gt;
&lt;br&gt;

&lt;p&gt;The reason was that on the homepage, there was a small sneak peek of the ranking, to engage users. There were also a couple of statistics shown, as the total number of laptops or offers analyzed. But how often did these numbers and results change? Every few hours. And how often did we calculate them? Every single page view. Huh. Adding an &lt;b&gt;HTML cache for the whole homepage&lt;/b&gt; worked like a charm, making it just a static HTML resource, that can be served by servers in a quite effortless way.&lt;/p&gt;

&lt;p&gt;Blackfire is a very good tool that &lt;b&gt;helps to find bottlenecks&lt;/b&gt;. To learn more, make sure to read our recent blog post &lt;a href="https://accesto.com/blog/my-homepage-is-slowing-down/" title="My homepage is slowing down"&gt;“My homepage is slowing down”&lt;/a&gt; in which our developer describes how he reduced page loading time by 93% in just 4 hours of work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: learn your user's behavior and use it to plan the performance
&lt;/h3&gt;

&lt;p&gt;Another optimization became easy when we analyzed data from Google Analytics. SzukamLapka generates personalized rankings based on the individual needs and preferences of the users. But according to Google Analytics, most of the users go for the predefined presets, like gaming, office, or mobile laptops. So why generate them for every user separately? &lt;b&gt;Adding a Redis cache&lt;/b&gt; for most frequently selected rankings reduced the computations by almost 70%.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: use CDN to cache your static resources
&lt;/h3&gt;

&lt;p&gt;Last but not least - we added &lt;a href="https://www.cloudflare.com/" rel="nofollow"&gt;Cloudflare&lt;/a&gt;. It is a service that provides an additional cache layer in front of the whole website and distributes all its static resources in CDNs. It makes the assets, like images or scripts, &lt;b&gt;highly available for users, and light for the server&lt;/b&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0-_fWg5P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ist62k5h52jrfsn8ajfo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0-_fWg5P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ist62k5h52jrfsn8ajfo.jpg" alt="Cloudflare reduced bandwidth by 60% and page load time by 80%" width="880" height="472"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;center&gt;Cloudflare reduced bandwidth by 60% and page load time by 80%&lt;/center&gt;
&lt;br&gt;

&lt;p&gt;Adding Cloudflare saved around 60% bandwidth traffic, making the website significantly lighter for the server. At the same time, the &lt;b&gt;user experience improved thanks to the reduction of the total load time to less than 1 second!&lt;/b&gt; To learn more about caching with Cloudflare, check the blog post of our CTO on &lt;a href="https://accesto.com/blog/top-5-hacks-to-fix-slow-web-applications/" title="Top 5 hacks to fix slow web applications"&gt;"Top 5 hacks to fix slow web applications"&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fast forward
&lt;/h2&gt;

&lt;p&gt;Thanks to this lesson learned, the website was adjusted, and right now can handle a &lt;b&gt;significantly large number of users&lt;/b&gt;. After a few years SzukamLapka is playing an important role in consumer decision making in Poland, generating millions in revenue for its partners. This year, SzukamLapka is launching its &lt;b&gt;global version&lt;/b&gt; in multiple countries around the world. Thanks to the lessons learned, it is prepared to become popular in the new markets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GJkft5A---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/istzk2zzi8hstzhb2nlv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GJkft5A---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/istzk2zzi8hstzhb2nlv.jpg" alt="PageSpeed" width="590" height="413"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;center&gt;Current version requires less than 1 second to be full interactive!&lt;/center&gt;
&lt;br&gt;
&lt;h2&gt;
  
  
  How about your web application?
&lt;/h2&gt;

&lt;p&gt;The story of the SzukamLapka startup &lt;b&gt;can happen to almost every online business&lt;/b&gt;. If you want to check how your web application can be hardened just drop me a message. At Accesto we look after, monitor, and improve web applications to make sure they are &lt;b&gt;secure, scalable, and ready for growth&lt;/b&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://accesto.com/contact/" id="let_us_help_you"&gt;Let us help you&lt;/a&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>devops</category>
      <category>performance</category>
    </item>
    <item>
      <title>Optimizing website? Careful with lazy loading!</title>
      <dc:creator>Piotr Gołofit</dc:creator>
      <pubDate>Thu, 05 May 2022 10:02:30 +0000</pubDate>
      <link>https://dev.to/accesto/optimizing-website-site-careful-with-lazy-loading-522g</link>
      <guid>https://dev.to/accesto/optimizing-website-site-careful-with-lazy-loading-522g</guid>
      <description>&lt;p&gt;A few weeks ago I was asked if Accesto could help to &lt;strong&gt;optimize a popular online store&lt;/strong&gt; selling natural cosmetics. Of course we can! I thought. That is what we do for a living, we improve existing web applications like SaaS, marketplaces, or eCommerce sites. Thus I thought it would be an optimization like every other. But it wasn’t.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick wins for website speed
&lt;/h2&gt;

&lt;p&gt;Although every online product is unique, they usually struggle with similar &lt;strong&gt;web performance pitfalls&lt;/strong&gt;. Non-optimal database queries, poor caching, too large images/resources, or unused imports, to name a few. Usually, there are &lt;strong&gt;a few things that can be fixed relatively quickly&lt;/strong&gt; and will result in quite a speed boost. Our CTO recently gathered such tweaks for you in his blogpost on &lt;a href="https://accesto.com/blog/top-5-hacks-to-fix-slow-web-applications/" rel="noopener noreferrer"&gt;top 5 hacks to fix slow web applications&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Top performance, which will hold the exponential growth of users, cannot be achieved overnight. But if your website is slowing down, and you haven't yet put a lot of effort into its optimization, there is a high chance that it can be refined in a quite reasonable time. Like in one of our &lt;a href="https://accesto.com/blog/my-homepage-is-slowing-down/" rel="noopener noreferrer"&gt;recent stories&lt;/a&gt; where we managed to &lt;strong&gt;decrease website loading time by 97% in just 4 hours of work&lt;/strong&gt;.&lt;br&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%2Ftfeniuf04lw58o9bt6k0.jpeg" 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%2Ftfeniuf04lw58o9bt6k0.jpeg" alt="Initial PageSpeed results"&gt;&lt;/a&gt; &lt;/p&gt;


&lt;center&gt;Initial PageSpeed results for online store selling natural cosmetics&lt;/center&gt;
&lt;br&gt;

&lt;p&gt;The case of natural cosmetics eCommerce seemed similar. The store owner sent me an email on Friday morning with the Google PageSpeed Insights report. It &lt;strong&gt;evaluated the overall site performance&lt;/strong&gt; on &lt;span&gt;61&lt;/span&gt;/100 for desktop, and &lt;span&gt;42&lt;/span&gt;/100 for mobile. Not a tragedy, but indeed a lot of room for improvement. Great! We will apply a few quick tweaks that will speed up the website and I will have good news for a customer even before the weekend!&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s start with a website audit &amp;amp; checking Core Web Vitals
&lt;/h2&gt;

&lt;p&gt;We already knew the &lt;strong&gt;overall score&lt;/strong&gt; for this website - our starting point for optimization. This score is a &lt;strong&gt;composition of 6 metrics evaluated by PageSpeed&lt;/strong&gt;, which for that particular site presented like this:&lt;/p&gt;

&lt;p&gt;](&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/50jualml4g4hqdi74z7f.jpeg" rel="noopener noreferrer"&gt;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/50jualml4g4hqdi74z7f.jpeg&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Quick look at the metrics and we see that &lt;strong&gt;4 out of 6 metrics look fine&lt;/strong&gt; (&lt;a href="https://web.dev/vitals/" rel="nofollow noopener noreferrer"&gt;Web Vitals&lt;/a&gt;), but the remaining two: &lt;em&gt;Speed Index&lt;/em&gt; and &lt;em&gt;Largest Contentful Paint (LCP)&lt;/em&gt; seem way too high. Understanding these metrics helps to &lt;strong&gt;decide where we shall start our optimization&lt;/strong&gt;, how much we should improve, and whether we can find some low-hanging fruits. Each of these &lt;strong&gt;metrics has a different weight&lt;/strong&gt; (importance). To see how exactly they influence the overall score you can use the &lt;a href="https://googlechrome.github.io/lighthouse/scorecalc/" rel="nofollow noopener noreferrer"&gt;scoring calculator&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%2Fsapdl8ov2n0ya5u81n8n.jpeg" 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%2Fsapdl8ov2n0ya5u81n8n.jpeg" alt="Lighthouse scoring calculator"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although PageSpeed evaluated both SI and LCP as poor ones, &lt;strong&gt;LCP has a higher weight&lt;/strong&gt; (25%), so improving it &lt;strong&gt;will have the highest impact on the overall score&lt;/strong&gt;. LCP is one of 3 important metrics introduced by Google, called &lt;strong&gt;Core Web Vitals&lt;/strong&gt;. Ok, but what exactly is this LCP? In short - it is the &lt;strong&gt;time at which the largest visible part of website content is painted&lt;/strong&gt;. This content could be an image, a large block of text, or any other element that occupies a significant part of the screen when the website loads. And the faster this element is displayed to the user, the better. &lt;/p&gt;

&lt;p&gt;Knowing that, we could assume that perhaps some product image or banner is too large, not scaled properly, or maybe not compressed. Or maybe images are served by PHP and not cached? Few basic checks should show us what is going on.&lt;/p&gt;

&lt;p&gt;We used a Lighthouse tool built-in Chrome web browser to &lt;strong&gt;profile the website&lt;/strong&gt;. We were looking for any performance pitfalls, but the audit results were… surprisingly good! &lt;strong&gt;The site wasn’t performing that bad at all!&lt;/strong&gt; Server response time was quite decent, images were served in a modern format and with proper sizing. All the site assets were properly optimized and served with proper cache policy. They even used some of the Progressive Web App capabilities to make the site work smoothly. &lt;br&gt;&lt;br&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%2Fp6lbxotaowietgkp75hb.jpeg" 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%2Fp6lbxotaowietgkp75hb.jpeg" alt="Lighthouse page speed optimization tool in Google Chrome"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;center&gt;Lighthouse built in Google Chrome Developer Tools can be used to audit the website performance&lt;/center&gt;
&lt;br&gt;

&lt;p&gt;Ok, so maybe it was a &lt;strong&gt;traffic related issue&lt;/strong&gt;? Maybe site speed is good as long as the number of users using it is low? Worth a try. It is quite a common situation, many websites are tested only on a small number of users. &lt;strong&gt;When the user base grows website starts to clog&lt;/strong&gt;. More on that topic you can read in my post on &lt;a href="https://accesto.com/blog/why-new-users-can-kill-your-web-application/" rel="noopener noreferrer"&gt;why new users can kill you web application&lt;/a&gt;. I did a few checks with the website load testing tool &lt;a href="https://k6.io/" rel="nofollow noopener noreferrer"&gt;k6.io&lt;/a&gt;, but nothing, still a decent response time. &lt;/p&gt;

&lt;p&gt;We also dug into the code to do some checks, but it only confirmed what was already obvious to us. Someone already &lt;strong&gt;did a great job to optimize this website&lt;/strong&gt;. We weren't asked just to do the basics, they know how to do that themselves. We were asked because none of the popular tweaks worked. Great, finally some challenge! &lt;/p&gt;

&lt;h2&gt;
  
  
  Profiling the website performance
&lt;/h2&gt;

&lt;p&gt;It was a time to dive deeper and do a step by step analysis. First of all, we had to figure out &lt;strong&gt;which site element was taken to calculate the LCP&lt;/strong&gt;. Performance tool built-in Google Chrome shows a precise timeline of all the elements requested, loaded, and painted. It also &lt;strong&gt;indicates moments when LCP and other metrics are measured&lt;/strong&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%2F9k9q8mu2a2ni2d23k51s.jpeg" 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%2F9k9q8mu2a2ni2d23k51s.jpeg" alt="Chrome Developer tools - performance timeline"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Analysis of this timeline confirmed our assumptions - the Largest Contentful Paint element was &lt;strong&gt;one of the product images on the homepage&lt;/strong&gt;. Why is this image displayed so late to the user? We checked the image format and size, and all looked good. Actually, the &lt;strong&gt;image itself was loaded in just 122 milliseconds&lt;/strong&gt;. So why did this image appear after almost 6 seconds if it loads in just a fraction of that?&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%2Fs4zi3r79p1m3uxywsvo5.jpeg" 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%2Fs4zi3r79p1m3uxywsvo5.jpeg" alt="Chrome Developer tools - network - timing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We analyzed the timing of that particular image, and what surprised us was that &lt;strong&gt;it started to load after 5.7s since the website was requested&lt;/strong&gt;. But why? Server response time was decent and all images should begin to load as soon as the browser receives the HTML from the server, right? Well…&lt;/p&gt;

&lt;h2&gt;
  
  
  Lazy loading done wrong
&lt;/h2&gt;

&lt;p&gt;The title of this article already revealed the diagnosis - yes, &lt;strong&gt;it was a lazy loader that delayed fetching that image&lt;/strong&gt;. But why? Isn’t the lazy loader one of the most common mechanisms that &lt;strong&gt;improve the website speed&lt;/strong&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%2Fxghlnkuxx0ndgmx1idve.jpeg" 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%2Fxghlnkuxx0ndgmx1idve.jpeg" alt="Lazy loader in action"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lazy loader prevents images from being fetched if they are not in the current viewport. This &lt;strong&gt;minimizes the amount of data transferred&lt;/strong&gt; in situations when only part of the site is seen by the user (eg. user views only the top of the page). Usually, it is good and makes the website load visibly faster. Usually...&lt;/p&gt;

&lt;p&gt;Unfortunately, it also &lt;strong&gt;has a side effect&lt;/strong&gt; that not every web developer is aware of. The lazy loader is simply a javascript code, that checks the image position and calculates whether it is in the current viewport. But this means that before the image is fetched, the web browser has to fetch the Javascript code and run it. The &lt;strong&gt;user won’t see the image until Javascript decides&lt;/strong&gt; that he should see it. And that causes a delay.  &lt;/p&gt;

&lt;p&gt;And of course, there are many ways to implement the lazy loader so that delay is minimized. But in our case, the code responsible for lazy loading used an external, third-party javascript library that had to be fetched first. And what is more, this code was implemented in an &lt;em&gt;onload&lt;/em&gt; event handler, which means that it was &lt;strong&gt;executed only after all website elements finished loading&lt;/strong&gt; - including styles, fonts, scripts etc. And that all took 5.7s. It's quite a long time to decide if an image should be downloaded or not, isn't it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Results of website optimization
&lt;/h2&gt;

&lt;p&gt;There are many ways to solve this problem. We could get rid of the dependency on the external library or execute the lazy loader earlier than in &lt;em&gt;onload&lt;/em&gt;. But the quick fix was to simply &lt;strong&gt;switch off the lazy loader for images that are always seen above the fold&lt;/strong&gt; (the portion of the webpage that is visible without scrolling). This simple solution took just one line of code and did a job. Just see how the website speed improved:&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%2Fauz1b9r6uq0jb9m03h78.jpeg" 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%2Fauz1b9r6uq0jb9m03h78.jpeg" alt="Final results of site speed optimization"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It took us a few hours to find the cause of the poor LCP and finally add this single line of code. Website optimization is usually not about coding, it is analytical work of searching, digging, and investigating. Of course, this wasn’t the end of the optimization, still room for improvement. But we were surprised &lt;strong&gt;how much Core Web Vitals score improved with a simple fix in a lazy loader&lt;/strong&gt;. This made me curious so I also checked other popular eCommerce websites and I was surprised. Many of them did have the same issue! Seems that lazy loader is seen as a great, out of the box solution for optimization, but &lt;strong&gt;very often added carelessly&lt;/strong&gt;. And adding it too quickly (too lazy?) may actually harm the overall site speed. &lt;strong&gt;So be careful and don’t add stuff to your site just because everyone does it!&lt;/strong&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%2F5ycwy143jw7dmz8tbsxo.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%2Fuploads%2Farticles%2F5ycwy143jw7dmz8tbsxo.jpg" alt="Email after successful website optimization"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>seo</category>
      <category>ux</category>
      <category>webdev</category>
      <category>webperf</category>
    </item>
    <item>
      <title>5 tools for Core Web Vitals to measure and improve website UX</title>
      <dc:creator>Piotr Gołofit</dc:creator>
      <pubDate>Thu, 14 Apr 2022 09:16:35 +0000</pubDate>
      <link>https://dev.to/accesto/5-tools-for-core-web-vitals-to-measure-and-improve-website-ux-3f26</link>
      <guid>https://dev.to/accesto/5-tools-for-core-web-vitals-to-measure-and-improve-website-ux-3f26</guid>
      <description>&lt;p&gt;Core Web Vitals are components of web analytics that belongs to &lt;a href="https://web.dev/vitals/" rel="nofollow"&gt;Web Vitals&lt;/a&gt;. Their primary purpose is to &lt;strong&gt;improve the experience of website visitors&lt;/strong&gt;. They apply to web pages displayed on desktop and mobile. The goal of CWV is to examine the page load length and accessibility before and after rendering. For website owners, high scores on these metrics influence the popularity of those sites by Google's algorithms. The friendlier a site is, the more willing users are to use it, which translates into a &lt;strong&gt;better ranking position on the search results page&lt;/strong&gt;. In that case, you should learn about 5 tools to check and optimize Core Web Vitals.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to measure Core Web Vitals metrics?
&lt;/h2&gt;

&lt;p&gt;Core Web Vitals is a topic we introduced in a &lt;a href="https://accesto.com/blog/what-are-core-web-vitals/"&gt;previous blog post&lt;/a&gt;. However, as a reminder. The metrics in question take into account:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  LCP - Largest Contentful Paint (time to render largest element on site)&lt;/li&gt;
&lt;li&gt;  FID - First Input Delay (delay of the first action)&lt;/li&gt;
&lt;li&gt;  CLS - Cumulative Layout Shift (page layout offset)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;LCP assesses a page's loading performance, focusing on the loading time of the biggest, main element (eg. blogpost cover photo). However, FID is the period during which visitors can engage with the website. FID is a statistic that counts keystrokes, taps, and clicks as input events. Thus, &lt;strong&gt;both of these indicators can be measured statically&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;CLS is a statistic that assesses a website's visual stability. The amount of apparent shifts during page loading has an impact on the total score. Hence, it stands out from other indicators; &lt;strong&gt;its measurement is done dynamically&lt;/strong&gt; because the current shift of the page is taken into account. So remember, when comparing the results of CLS measurement, to constantly compare them in the same fragment of the page during its actual use, and not during the static loading.&lt;/p&gt;

&lt;p&gt;If you're curious about a specific example of optimizing the website of a popular natural cosmetics store, you can &lt;a href="https://accesto.com/blog/optimizing-ecommerce-site-careful-with-lazy-loading/"&gt;follow our work here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools to check and optimize Core Web Vitals
&lt;/h2&gt;

&lt;p&gt;Although Web Vitals are a relatively recent development in aspects related to user experience, many tools or plug-ins are now available to help generate analytics. Google's principal web developer tools currently support Core Web Vitals measurement, making it easier to detect and address user experience concerns.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://developers.google.com/web/tools/chrome-user-experience-report/" rel="nofollow"&gt;Chrome UX Report (CrUX)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Chrome UX Report (CrUX) is a public collection of real-world data showing the user experience when visiting websites. All of the data collected in CrUX &lt;strong&gt;comes from users who have consented to download data in specific areas&lt;/strong&gt;. By actually measuring site activity, it is possible to verify the actual Core Web Vitals indicator data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HeHmcdda--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5dlm3t2gw71ww99zpzom.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HeHmcdda--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5dlm3t2gw71ww99zpzom.jpg" alt="CLS Optimization results in DataStudio" width="880" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Google Chrome User Experience Report data on &lt;a href="https://console.cloud.google.com/bigquery?p=chrome-ux-report&amp;amp;pli=1" rel="nofollow"&gt;BigQuery&lt;/a&gt; is a collection of details about the output for CWV and is available in monthly snapshots.&lt;/p&gt;

&lt;p&gt;The CrUX API, on the other hand, thanks to its daily update, allows you to investigate the source or URL and segment that data by various factors.&lt;/p&gt;

&lt;p&gt;The most recommended way to calculate how visitors perceive your site is to measure its &lt;strong&gt;performance in the field as users load and interact with the page&lt;/strong&gt;. This type of measurement is commonly referred to as Real User Monitoring - or RUM for short. Even if you don't have RUM on your site, CrUX can provide a quick and easy way to evaluate Core Web Vitals.&lt;/p&gt;

&lt;p&gt;A tool to visualize data, for example, based on reports like CrUX, is &lt;a href="https://marketingplatform.google.com/about/data-studio/" rel="nofollow"&gt;Data Studio&lt;/a&gt;. It allows you to create dashboards based on source files and thus capture trends in user behavior.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AHsGKmXD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t9nxwxddmitin6qegy0m.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AHsGKmXD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t9nxwxddmitin6qegy0m.jpg" alt="CLS Optimization results in DataStudio" width="880" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://search.google.com/search-console/welcome"&gt;Google Search Console&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Based on actual data from CrUX, Search Console's Core Web Vitals report may help you &lt;strong&gt;discover groupings of pages on your site that want improvement&lt;/strong&gt;. The performance of URLs is divided into three categories: metric type, status, and URL group (groups of similar web pages).&lt;/p&gt;

&lt;p&gt;LCP, FID, and CLS are three Web Vitals indicators used in the report. A URL is removed from the account if it does not contain the required reporting data for these metrics.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v0Md6OiG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jiwo8pv8t8clrbdkw13n.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v0Md6OiG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jiwo8pv8t8clrbdkw13n.jpg" alt="Core Web Vitals in Google Search Console" width="880" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://developers.google.com/speed/pagespeed/insights/" rel="nofollow"&gt;PageSpeed Insights&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;With PageSpeed Insights, it is possible to get both lab and field site performance. The PSI tool provides data on what site visitors' experience is like on both mobile and desktop devices. The PSI report contains information about &lt;strong&gt;meeting thresholds of adequate user experience&lt;/strong&gt;, which is indicated by passing or failing Core Web Vitals assessment. The result of each analysis is a set of recommendations that should be implemented to improve the indicators (using Lighthouse).&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://chrome.google.com/webstore/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk?hl=pl" rel="nofollow"&gt;Lighthouse&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Lighthouse is an &lt;strong&gt;automatic website audit tool that assists developers in diagnosing issues and identifying ways to improve their sites' user experience&lt;/strong&gt;. The tool provides lab data of the metrics. Two of them - LCP and CLS - are lab implementations of Core Web Vitals and allow you to draw diagnostic conclusions about the performance score calculation. The third metric in Lighthouse is TBT (total loading time), which correlates strongly with the FID field metric. Implementing the changes recommended in the Lighthouse report allows you to optimize and improve the user experience of your site visitors.&lt;/p&gt;

&lt;p&gt;Lighthouse is &lt;strong&gt;automatically installed in all versions of the Google Chrome browser&lt;/strong&gt; as a part of Chrome DevTools (version 60 and above). You need to right-click and choose "explore" and then go to the "Audits" tab.&lt;/p&gt;

&lt;p&gt;It is also possible to install a plug-in for Chrome. It works analogously. The main difference is that you launch it from the top right corner of the browser (it's a bit more convenient). The principle of operation is the same.&lt;/p&gt;

&lt;p&gt;Lighthouse is worth using in conjunction with the "Performance" tab, where it is possible to &lt;strong&gt;track the total page load so that step by step&lt;/strong&gt;, you can catch possible elements to optimize.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--86_Hji1r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1cztnc9q761efq6mpe60.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--86_Hji1r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1cztnc9q761efq6mpe60.gif" alt="Optimizing Core Web Vitals - Chrome Performance Tab" width="700" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://chrome.google.com/webstore/detail/web-vitals/ahfhijdlegdabablpippeagghigmibma" rel="nofollow"&gt;Chrome Web Vitals Extension&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Web Vitals extension is dedicated to the &lt;a href="https://www.google.com/chrome/" rel="nofollow"&gt;Google Chrome&lt;/a&gt; desktop version. It works great in the &lt;strong&gt;early diagnostic stages when it is possible to catch basic errors&lt;/strong&gt;. The extension is available to install from the &lt;a href="https://github.com/GoogleChrome/web-vitals-extension" rel="nofollow"&gt;GitHub&lt;/a&gt; repository.&lt;/p&gt;

&lt;p&gt;As of September 2021, &lt;a href="https://analytics.google.com/analytics/web/provision/#/provision" rel="nofollow"&gt;Google Analytics&lt;/a&gt; does not have web vitals tracking, but with the library's help from GitHub, you can send this data as events to Analytics. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CjZTRVZB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/umvpmd93wsw44e6m6xdw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CjZTRVZB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/umvpmd93wsw44e6m6xdw.jpg" alt="Core Web Vitals Plugin" width="880" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Your favorite SEO tool
&lt;/h2&gt;

&lt;p&gt;Using the tools mentioned above dedicated to Core Web Vitals should give you a complete picture of the state of your sites in terms of UX. However, SEO tools also play a significant measure. When it comes to rankings, it's always &lt;strong&gt;better to optimize your site from multiple angles rather than just one&lt;/strong&gt;. So let's look at how popular SEO tools enable you to optimize for Core Web Vitals.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Wi0aYhCW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l58q3uaxe8plgds0a22q.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wi0aYhCW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l58q3uaxe8plgds0a22q.jpg" alt="Core Web Vitals Optimization" width="880" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.semrush.com/" rel="nofollow"&gt;SEMrush&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;SEMrush is one of the most preferred cloud SEO tools by various companies. Thus, in order not to lower its popularity, it has been customized for Core Web Vitals.&lt;/p&gt;

&lt;p&gt;After performing a general audit of a given site, it is necessary to select On Page &amp;amp; Tech SEO &amp;gt; Site Audit, choose the domain to be analyzed, and then select the type of user agent. Once you have the report, you will have a &lt;strong&gt;graphical representation of the current state of CWV&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It's worth reminding that SEMrush &lt;strong&gt;doesn't use Google's API and gets all the lab data from Lighthouse&lt;/strong&gt;. Therefore it's impossible to get the FID report. While SEMrush works well for internal use, it is impossible to export the report. The only way to present the statistics to website owners, for example, is to take screenshots. SEMrush, in its statistics, shows only data for the Core Web Vitals page. Thus it is a tool that supports or offers a slice of statistics for Core Web Vitals.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.screamingfrog.co.uk/" rel="nofollow"&gt;Screaming Frog&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Screaming Frog is a tool that allows you to audit your website for all aspects of SEO thoroughly. It is also possible to verify CWV coefficients. For this purpose, data is taken directly from Google; downloading Google's API key &lt;strong&gt;makes it possible to check up to 25,000 pages per day&lt;/strong&gt;. The report of a given website in Screaming Frog contains a complete set of recommendations for better domain optimization.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;inability to create an aggregated site speed report&lt;/strong&gt; is one of the drawbacks of this tool. However, if you need one, it is necessary to transfer the information to Excel and create a table manually. However, it is possible to download reports with Page Speed optimization options and submit them to developers to implement the recommended solutions.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.link-assistant.com/website-auditor/" rel="nofollow"&gt;WebSite Auditor&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;WebSite Auditor is an SEO measurement tool, mainly dedicated to desktop devices. If you have ever used it, you know that a large amount of information to process is an ideal environment to work in. WebSite Auditor reports on over 80-page speed metrics and uses Google's API to download the data, so there are &lt;strong&gt;no discrepancies in the statistics provided by the two tools, and the analysis is automatic&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;WebSite Auditor allows you to verify all addresses linked to a single site in a single analysis. Hence there is no need to prove each URL as with PageSpeed Insight or SEMrush.&lt;/p&gt;

&lt;p&gt;Unlike the previously mentioned SEO tools, WebSite Auditor &lt;strong&gt;allows you to create a bulk Core Web Vitals analysis report in CSV format&lt;/strong&gt;. Thus, this tool will work great if the information you make needs to be frequently sent to clients or other teams.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is it worth it to measure Core Web Vitals?
&lt;/h2&gt;

&lt;p&gt;Measuring Chrome Web Vitals is a must if you want to rank properly in Google searches. However, don't forget that CWV is not the only metric that affects your domain data. Google also considers &lt;strong&gt;mobile-friendliness, HTTPS, secure browsing, and non-intrusive interstitials&lt;/strong&gt; in its analysis. However, optimizing Core Web Vitals is beneficial, even if the ranking impact turns out to be lower than expected because it affects user experience. Also, visitors are more likely to navigate the site if you reduce various aspects that hinder them. And organic traffic is still one of the main factors considered in Google's analysis.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>ux</category>
      <category>webdev</category>
      <category>webperf</category>
    </item>
    <item>
      <title>How Docker reduces development costs?</title>
      <dc:creator>Piotr Gołofit</dc:creator>
      <pubDate>Thu, 07 Apr 2022 13:18:43 +0000</pubDate>
      <link>https://dev.to/accesto/how-docker-reduces-development-costs-e90</link>
      <guid>https://dev.to/accesto/how-docker-reduces-development-costs-e90</guid>
      <description>&lt;p&gt;Docker is a great tool and in recent years became almost a must-have for many development teams and projects. Why? For many reasons, which I share in detail in one of my &lt;a href="https://accesto.com/blog/what-is-docker-and-why-to-use-it/"&gt;previous posts&lt;/a&gt;. Today I would like to focus purely on one aspect… &lt;em&gt;money&lt;/em&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Can Docker save my money?
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;TLDR; Yes, it can.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But let’s see how, and more importantly, how much.&lt;/p&gt;

&lt;p&gt;To do so, I would like to introduce you to an imaginary project called &lt;em&gt;RevoCRM&lt;/em&gt;. I will use it as a case study, to calculate the impact of Docker on software development costs. Talking about money is always a delicate topic, so I won’t share the details of a particular project, but rather a composition of my experience from a couple of projects I had a chance to work on.&lt;/p&gt;

&lt;p&gt;Let’s say that &lt;em&gt;RevoCRM&lt;/em&gt; is a gaining popularity CRM tool, for a small-medium size business. It is distributed in a SaaS model and has ~5,000 active subscriptions globally, mostly in the US and Europe. Although &lt;em&gt;RevoCRM&lt;/em&gt; is an Amsterdam-based startup, its development team is fully remote and consists of developers from different countries, mostly from eastern Europe. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--scOO3jUo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0nfxy9c2pv0l2e02ylw1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--scOO3jUo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0nfxy9c2pv0l2e02ylw1.png" alt="RevoCRM project overview" width="824" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Currently, &lt;em&gt;RevoCRM&lt;/em&gt; uses Docker both for the development and as a part of the hosting infrastructure. But it wasn’t always like that and in this study, I will estimate some of the costs saved thanks to Docker. I will consider the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;costs of employee churn;&lt;/li&gt;
&lt;li&gt;costs of everyday development;&lt;/li&gt;
&lt;li&gt;costs of testing and quality assurance;&lt;/li&gt;
&lt;li&gt;costs of introducing infrastructure changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s see in detail, how much can be saved thanks to Docker.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Docker reduces the costs of employee churn?
&lt;/h2&gt;

&lt;p&gt;Introducing the new employee to the team is always a time-consuming process. This is especially true regarding software developers. Software products, like &lt;em&gt;RevoCRM&lt;/em&gt;, consist of many different components like databases, services, third-party libraries, and other tools. Each newcomer, before writing his/her first line of code on the project has to install and configure all of these on the computer. Thanks to Docker, right now it is all reduced to one single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But in the old, docker-less days of &lt;em&gt;RevoCRM&lt;/em&gt;, it took between 4 to 5 days to make the whole platform up and running on a developer's computer. &lt;em&gt;RevoCRM&lt;/em&gt; development team is fully remote and each developer has its favorite operating system and configuration. Thus the process was slightly different for each employee. But usually, it was a collaborative work of at least a few people, to help the new developer set up the project on his machine. On average, each new developer spent 36 working hours of his time, and additional 24 hours of other team members trying to help him, which results in 60 hours spent on the initial setup. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yN8XfWP_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0mxgluigg1m2m0zrhw0k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yN8XfWP_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0mxgluigg1m2m0zrhw0k.png" alt="Docker compose project setup" width="880" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;RevoCRM&lt;/em&gt; development team consists of 8 developers (6 back-end, and 2 front-end developers) and 2 testers. Their average employee churn rate is 20%, which means that every year they introduce 2 new team members. Knowing that the mean hourly rate of a single &lt;em&gt;RevoCRM&lt;/em&gt; employee is €30, we can calculate the overall yearly cost saved thanks to Docker:&lt;/p&gt;

&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2 developers / year × 60 hours × €30 / hour = €3,600 / year&lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;Not that much? Bear with me because savings start to accumulate quite quickly when the newcomer and his teammates start their everyday work on new features. &lt;/p&gt;

&lt;h2&gt;
  
  
  How Docker reduces the costs of everyday development?
&lt;/h2&gt;

&lt;p&gt;Unfortunately, Docker won’t do the coding for you, you will still need developers in your project team 😉 But what Docker can do, is to ensure that whenever one of the developers introduces any changes to the project configuration it will automatically be available for other developers. Before the &lt;em&gt;RevoCRM&lt;/em&gt; team switched to Docker, they were constantly struggling with changes in the technology stack. When a new integration, or a database, or some other service was added to &lt;em&gt;RevoCRM&lt;/em&gt;, it did not work automatically for all the developers. &lt;/p&gt;

&lt;p&gt;Sometimes a small change could prevent the whole team from focusing on their own tasks for a decent amount of time. Most fierce were the fights between back-end and front-end developers. Each side complained that the other one always adds things that do not work out of the box on their machines. Struggling with these technical issues took on average 5 working hours per month, for each developer. Introducing Docker helped to save:&lt;/p&gt;

&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5 hours × 8 developers × €30 / hour × 12 months = €14,400 / year&lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;Docker clearly helps to introduce changes to the technical stack with fewer complications to other team members. But it can also make the change easier for the developer that introduces it. Instead of searching for manuals on how to install some new database server, developers can use one of the available templates (Docker Images) from a publicly available repository called &lt;a href="https://hub.docker.com/" rel="nofollow"&gt;Docker Hub&lt;/a&gt;. This can save a day of work every quarter or so. &lt;/p&gt;

&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8 hours × 4 quarters × €30 / hour = €960 / year&lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;Docker helps to reduce the cost of coding. And what about further phases of the development process? Does it also bring some savings in the testing phase?&lt;/p&gt;

&lt;h2&gt;
  
  
  How Docker reduces the costs of testing and QA?
&lt;/h2&gt;

&lt;p&gt;One of the most important advantages of Docker is that it ensures consistent behavior of software on all the machines. This is true for the developers’ computers, as well as for the test servers. Why is it that important? Because it significantly reduces the time of testing and quality assurance. Have you ever heard of the famous phrase “Strange, it worked well on my machine”? It is a very common issue. Testers blame developers for delivering faulty code but in many cases, that code actually did work on developers' local machines. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--14RabZqd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c0i9xj6j1ojy29jnmlje.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--14RabZqd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c0i9xj6j1ojy29jnmlje.png" alt="Consistency with docker containers" width="880" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before the &lt;em&gt;RevoCRM&lt;/em&gt; team introduced Docker, almost 17% of all User Stories delivered for testing had to be rejected just because testers were unable to make them run and test properly. These Stories had to be then refined by developers, and once again delivered for testing. &lt;/p&gt;

&lt;p&gt;The whole ping-pong process was taking approximately 2.5 hours per each problematic User Story delivered for testing. &lt;em&gt;RevoCRM&lt;/em&gt; development team is actually divided into two smaller teams, each with 4 developers and one tester. And each of these teams has the capacity of approx. 10 User Stories delivered per month. So the savings in the testing process after introducing Docker looks like this:&lt;/p&gt;

&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2 teams × 10 US × 17% failure rate × 2.5 hours × 12 months * €30 / hour = €3060 / year&lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;Also, the &lt;em&gt;RevoCRM&lt;/em&gt; developers were able to better test features themselves, ahead of delivering these to dedicated testers. This was possible as Docker's setup made end-to-end testing simpler for them. Previously, backend developers focused only on introducing API changes but were not validating if these work well with the front end. This reduced overall issues and time spent on bug fixes by almost 10%. By then each developer was spending 20 hours per month on fixes and refinements. Thanks to Docker the time and cost savings were significant: &lt;/p&gt;

&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8 developers × 20 hours × 10% improvement × 12 months * €30 = €5760 / year&lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;Development and testing savings add up to quite a sum, and there is one more aspect that Docker helps a lot with - the infrastructure. &lt;/p&gt;

&lt;h2&gt;
  
  
  How Docker reduces the costs of infrastructure changes?
&lt;/h2&gt;

&lt;p&gt;Speaking of hosting infrastructure, Docker, combined with additional tools (like Kubernetes or Docker Swarm) brings multiple benefits. Ease of infrastructure management, ease of scaling… just to name a few. I won’t describe all these in detail as it would require a separate blog post. Instead, I will focus on two particular aspects: great portability and lack of vendor locking. &lt;/p&gt;

&lt;p&gt;Not so long ago, the CTO of &lt;em&gt;RevoCRM&lt;/em&gt; decided to improve their disaster recovery plan. This was perhaps inspired by the recent event that happened to the popular hosting provider OVH. One of their data centers went on fire and burned down completely, leaving a lot of their customers with data losses and services being down for a few days. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;If it happened to OVH it may happen to everyone&lt;/em&gt; - he thought. And thus, he decided to have at least two hosting providers at a time. Fortunately, &lt;em&gt;RevoCRM&lt;/em&gt; was already running on Docker and Kubernetes. Thanks to that, the process of recreating the infrastructure with a second hosting provider was not only possible (no vendor locking), but also was a relatively simple task (because of the portability of Docker containers). Instead of spending a week or so on configuration, the whole process took just around 3 hours! Although it was a one-time job, different infrastructure changes happen every few months, or at least once per year (especially if your start-up has plans on scaling). This particular &lt;strong&gt;change brought €2500 of savings&lt;/strong&gt;. Instead of hiring an external consultant to do the job, the CTO managed to set up the environment himself. And now he can sleep soundly 😉&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary of savings
&lt;/h2&gt;

&lt;p&gt;Ok, time for the final answer on how much money can you save thanks to a Docker? Let’s summarize all the &lt;em&gt;RevoCRM&lt;/em&gt; savings: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZmBGmGpS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d1uc0plnkv0t9mpgkea4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZmBGmGpS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d1uc0plnkv0t9mpgkea4.png" alt="RevoCRM savings summary thanks to Docker" width="824" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Over €30,000 saved each year thanks to Docker. It is approximately &lt;strong&gt;5% of all yearly development costs&lt;/strong&gt; of &lt;em&gt;RevoCRM&lt;/em&gt;. Or in other words, free additional 3 weeks of development hours. Hours that can be spent on at least a few additional features that can be delivered to your end-users each year.&lt;/p&gt;

&lt;h2&gt;
  
  
  Will Docker reduce the costs also of my project?
&lt;/h2&gt;

&lt;p&gt;Still not convinced that Docker would be a good solution for your development team? Although Docker is a very useful tool and may help you reduce development costs, there are of course some cases in which it won’t be the best choice. Make sure to read my previous post on &lt;a href="https://accesto.com/blog/when-to-use-and-when-not-to-use-docker/"&gt;When to use and when not to use Docker?&lt;/a&gt; to see if your project will benefit from Docker. &lt;/p&gt;

&lt;p&gt;From my experience, many development teams complaining about Docker simply don't use it properly. In such a case, all the money you could save thanks to Docker may be the lost opportunity. Thankfully, our CTO decided to write a &lt;a href="https://accesto.com/books/docker-deep-dive/"&gt;book on Docker optimization&lt;/a&gt;. In his book, Michal explains how to work with Docker images and containers to make the most of them. Make sure to recommend it to your dev team. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://accesto.com/books/docker-deep-dive/"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5LcruaPK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tpie7g4lkk0g2xgmdiwm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5LcruaPK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tpie7g4lkk0g2xgmdiwm.png" alt="Docker Deep Dive Book" width="880" height="446"&gt;&lt;/a&gt;&lt;/p&gt;



</description>
      <category>docker</category>
      <category>devops</category>
    </item>
    <item>
      <title>Technical Debt - the silent villain of web development</title>
      <dc:creator>Piotr Gołofit</dc:creator>
      <pubDate>Wed, 23 Mar 2022 10:13:20 +0000</pubDate>
      <link>https://dev.to/accesto/technical-debt-the-silent-villain-of-web-development-7lm</link>
      <guid>https://dev.to/accesto/technical-debt-the-silent-villain-of-web-development-7lm</guid>
      <description>&lt;p&gt;Over 4 years ago our CTO wrote a blog post claiming that technical debt is the number one reason &lt;a href="https://accesto.com/blog/technical-debt-the-number-one-reason-why-software-development-projects-get-derailed/"&gt;why software development projects get derailed&lt;/a&gt;. Years have passed since, and I can say that he couldn't be more right. Although I see that business owners get more familiar with the term technical debt, it quite often plays the role of an elephant in the room. And in the long run, such stories rarely have happy endings. So if you don’t know what the technical debt is, or you do but you don’t think you should worry about this villain - better read further. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is technical debt?
&lt;/h2&gt;

&lt;p&gt;10 years of leading web development projects gave me the knowledge of how various technical decisions influence web applications development in the long run. I observed how hours of work invested in code quality and good software architecture enable future growth of software products and their business success. At the same time, I followed the stories of other products struggling to scale up and develop further due to all the shortcuts taken in the early stages of development. &lt;/p&gt;

&lt;p&gt;The lesson is simple - &lt;strong&gt;the more hours you save by doing things quick-and-dirty today, the more difficult the product development will be in the future&lt;/strong&gt;. All the shortcuts taken, all the savings on code or architecture quality, accumulate as a &lt;strong&gt;metaphorical technical debt&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rTUKNfTi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0wl4owxeum5paf5mrfa7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rTUKNfTi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0wl4owxeum5paf5mrfa7.jpg" alt="Technical debt impact on productivity" width="880" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Technical debt impact on productivity&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;Ward Cunningham, an author of that metaphor, highlighted its &lt;strong&gt;similarity to the financial debt and interest that you have to pay in the future&lt;/strong&gt;. A technical loan, that you take out by saving some coding hours today, will make further development and product maintenance more difficult, time-consuming, and expensive. And that is the interest that you pay for cutting corners. But unlike financial, technical debt is way harder to measure and track, so it’s costs are often overlooked or neglected. Let’s see them in more detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the costs of technical debt?
&lt;/h2&gt;

&lt;p&gt;Technical debt accumulates slowly, usually invisible at the beginning. &lt;strong&gt;Gradually making product development less efficient and thus more expensive&lt;/strong&gt;. Like in a popular among software developers anecdote of a boiling frog. If placed in boiling water, the frog will jump out, but if it is placed in cold water and slowly heated, it will overlook the danger and may be cooked to death. And this is very true for the web applications, slowly boiled in a soup of technical debt, getting way too expensive to develop and unprofitable to maintain. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ln2uxaVW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6988h4ukkji615wddg9j.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ln2uxaVW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6988h4ukkji615wddg9j.jpg" alt="Boiling frog" width="880" height="515"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Anecdote about a boiling frog - metaphor for IT projects slowly boiled in a technical debt soup&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;We experienced it ourselves at Accesto when we had to shut down our own successful product a couple of years ago. It was a popular SaaS tool for the online gaming industry, used by the majority of hosting providers in Poland. Despite the great market traction, we had to discontinue its sales, when &lt;strong&gt;costs of development and maintenance outgrew all the profits&lt;/strong&gt;. After years since we shut it down, we are still getting requests from people willing to pay for it. It was bitter, but a valuable lesson on the technical debt for us. &lt;/p&gt;

&lt;p&gt;These days I see more and more &lt;strong&gt;web products struggle to grow due to the technical debt&lt;/strong&gt;. The startup boom for web applications and SaaS products continues for over two decades now. Many of the products that made it to the market did it by taking technical shortcuts. &lt;strong&gt;The pressure to deliver quickly and within the budget has its side effects&lt;/strong&gt; and now some of these products struggle with growing costs of technical debt. &lt;/p&gt;

&lt;p&gt;Our main focus at Accesto is to help such products grow, by &lt;a href="https://accesto.com/expertise/"&gt;fighting technical debt&lt;/a&gt; and reducing its costs. Among such costs the most common are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Decreased web development pace&lt;/strong&gt; - slowed down by technical roadblocks, rework, hard to understand code, and focus on workarounds instead of efficient development&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Longer time to market for new features&lt;/strong&gt; - slow development and constant bug fixing makes it difficult to meet any deadlines for releasing new features&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expensive maintenance&lt;/strong&gt; - finding and fixing bugs becomes more difficult, lack of automated tests adds a cost of manual testing work with every change in the code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliability issues and recurring bugs&lt;/strong&gt; - fixing one bug leads to another&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The increased cost of introducing changes&lt;/strong&gt; - even small update may require changes in many places, that’s because code is tangled with unnecessary dependencies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hidden performance defects&lt;/strong&gt; - software that worked well for a few users starts to clog when the amount of data and number of users increases, app slows down&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Higher uncertainty&lt;/strong&gt; - more difficult to estimate and plan the web development work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technology locking&lt;/strong&gt; - poor code or architecture makes it hard to update current technology or migrate to a more modern one&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risk of security defects&lt;/strong&gt; - old technology makes web app vulnerable to breaches, unreadable code makes it difficult to secure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decreasing customer satisfaction&lt;/strong&gt; - due to recurring bugs, poor performance, and a long time to market for new features, competition won’t miss that chance!!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decreased development team motivation&lt;/strong&gt; - everyday work becomes a constant fight with messy code, bug fixing, and trying not to break something &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The increased cost of hiring web developers&lt;/strong&gt; - poor availability and high cost of hiring specialists with expertise in old technology and willing to work with legacy code &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost of knowledge transfer&lt;/strong&gt; - lack of documentation and unreadable code makes it hard to understand the project and introduce new team members&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inability to respond to market opportunities&lt;/strong&gt; - the cost of missed chances due to long time to market for all changes and features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gIlD6Ijb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m09xumwuousdii40perk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gIlD6Ijb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m09xumwuousdii40perk.jpg" alt="Debt vs team velocity" width="880" height="465"&gt;&lt;/a&gt; &lt;em&gt;Development hours wasted on on rework and fixing defects&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;If you see that your web development slows down, the number of bugs increases, or your dev team starts to complain about the code, &lt;strong&gt;do not neglect these symptoms!&lt;/strong&gt; Further in this article, I will show you some ways of fighting technical debt. But before fixing things, let’s first make sure it won’t get worse. To do so, we have to eliminate (or at least reduce) the drivers of technical debt in your project. &lt;/p&gt;

&lt;h2&gt;
  
  
  Where does the technical debt come from?
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;“Don't overcomplicate it, just do the simplest!”&lt;/em&gt;  &lt;/p&gt;

&lt;p&gt;&lt;em&gt;“No matter what, we have to publish it before black Friday!”&lt;/em&gt;  &lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Why is the estimate so high? It’s a simple feature!”&lt;/em&gt;  &lt;/p&gt;

&lt;p&gt;Do these or similar phrases sound familiar to you? &lt;strong&gt;Time and budget pressure are among the key drivers of technical debt.&lt;/strong&gt; Code quality and deliberate planning simply stand in a way of such short term, more urgent goals. This forces developers to take the shortcuts and implement features in an easier, quick-and-dirty way. When pressure is high, even experienced developers take that path, usually with a hope that this code will be improved (refactored) someday. But will it?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wKwixCGZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s307etyqwgfbklxffndv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wKwixCGZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s307etyqwgfbklxffndv.jpg" alt="Big ball of mud" width="880" height="465"&gt;&lt;/a&gt; &lt;em&gt;Big ball of mud - a result of a quick-and-dirty development&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;Experienced developers are at least conscious when the debt is introduced. But another &lt;strong&gt;popular driver of technical debt is the lack of qualified specialists in the product team&lt;/strong&gt;. Lack of certain skills generates hidden debt that nobody is aware of, which is very risky. This is why projects run by junior web developers may happen to be cheaper at the beginning but quickly lead to the roadblocks very expensive to overcome. &lt;/p&gt;

&lt;p&gt;Some would also say that technical debt comes simply from the laziness of developers. Although it may be true in some cases, I would argue if it is that popular. It may be the case with junior developers, but &lt;strong&gt;the more experienced developers are, the more they like clean and well thought out solutions&lt;/strong&gt;. I noticed that instead of cutting the corners they rather tend to overthink and over-engineer. &lt;/p&gt;

&lt;p&gt;For sure there is one more catalyst of technical debt - &lt;strong&gt;a constant change in business goals and requirements&lt;/strong&gt;. And in a similar way to time and budget pressure, it may lead even experienced team members to get off the track of deliberate development. The more thought a developer puts in good code design and implementation, the bigger demotivation when he has to throw it away when requirements change. If this happens very often, team members start to care less about the quality and long term impacts of the code they create. There is a lot of truth in saying that the only constant in IT projects is the change. But it is &lt;strong&gt;important to discuss all the business goals and underlying purposes with the development team&lt;/strong&gt;. This will allow them to assess the required quality and foresee the probable changes. Otherwise, they may spend hours meticulously designing something that is just some proof of concept. At Accesto, we make sure that all developers know WHY do they implement particular featrues and that's maybe why they &lt;a href="https://accesto.com/blog/why-developers-work-over-7-years-at-accesto/"&gt;stay with us for many years&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Short term benefits of keeping the budget low while developing features quickly are hard to argue, especially in the early MVP stages of the software product. But like with finance, if you neglect the long term perspective and base your cash flow only on the liabilities, &lt;strong&gt;you will end up in the debt spiral&lt;/strong&gt;, sooner or later. So should you avoid technical debt at any cost? &lt;/p&gt;

&lt;h2&gt;
  
  
  Should I avoid a technical debt at any cost?
&lt;/h2&gt;

&lt;p&gt;The analogy of financial debt is very true here. Should you avoid any financial debts? If you can run and develop your business without taking out any loans then good for you. But in many cases to grow, to develop, you cannot avoid banks. You are not waiting until you save enough money to open a new production line. You take out a loan, you open a line, and you use the profits out of it to pay off the debt.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--obMSyoVK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vymp7z93r213zztfs3rm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--obMSyoVK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vymp7z93r213zztfs3rm.jpg" alt="Initial debt and further refactoring" width="880" height="465"&gt;&lt;/a&gt; &lt;em&gt;Paying off the technical debt accumulated in MVP stages helps to restore good development pace&lt;/em&gt;&lt;/p&gt;



&lt;p&gt;Same is with technical debt, &lt;strong&gt;you accept it to make thighs faster&lt;/strong&gt;. Especially in the early stages of the business, or when you want to test a new business opportunity. If you are not sure whether some product or feature will get the market traction it is usually a wise choice to limit the initial investment and cut some technical corners. As Neil Ernst mentioned in the &lt;a href="https://insights.sei.cmu.edu/sei_blog/2015/07/a-field-study-of-technical-debt.html" rel="nofollow"&gt;Carnegie Mellon University field study&lt;/a&gt;, technical debt &lt;em&gt;conceptualizes the tradeoff between the short-term benefit of rapid delivery and long-term value&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;But when decide on that tradeoff, and deliberately accept technical debt, please remember that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When you take a bank loan, you can’t forget about it. If you do, costs are even higher and you won’t risk that situation. But it is way easier to forget about the technical debt, as the interest rate and cost of code shortcuts are not that obvious. So remember, &lt;strong&gt;whenever you agree on some technical debt, do it considerably, note it, manage it, and pay it off&lt;/strong&gt;. Better sooner than too late. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can't take out loans over and over again and it is true for both finance and software development. If you borrow too much, next time your financial institution will deny lending you more. With technical debt, there is no special institution that will limit your liabilities. So &lt;strong&gt;listen to the development team&lt;/strong&gt; instead. Do they complain about the code quality? Do they mention the refactoring? Talk to them, &lt;strong&gt;there is no one else to tell you when too much is too much&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;All debts must be paid - simple as that. You borrow money to scale your business and pay off that debt later on. Same for technical debt. You release a new feature, you get more users, more revenue, and... you pay off the technical debt. It is always tempting to leave it as it is, do not improve that feature, just start working on another one. But that will lead to a metaphorical big ball of mud and trust me, it is not something that you want your web application to be. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jkGhN5dc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wzva1c5pb0wenon03sfw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jkGhN5dc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wzva1c5pb0wenon03sfw.jpg" alt="Technical debt quadrant" width="880" height="465"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;center&gt;Technical debt quadrant (&lt;a href="https://martinfowler.com/bliki/TechnicalDebtQuadrant.html" rel="nofollow"&gt;by Martin Fowler&lt;/a&gt;) - different types of technical debt&lt;/center&gt;
&lt;br&gt;

&lt;p&gt;Not all debts have to be avoided and in many cases a &lt;strong&gt;deliberate technical debt can have a positive effect on overall product and business development&lt;/strong&gt;. But only if you are going to pay it off. But how to do it?&lt;/p&gt;

&lt;h2&gt;
  
  
  How to pay off the technical debt?
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;The only solution is to rewrite it from scratch&lt;/em&gt; - that may be a popular phrase that you will hear from software developers when they see the codebase of your web application. Don’t be surprised, developers usually don’t like working with old, legacy code, but love new, greenfield projects. &lt;strong&gt;Rewriting from scratch&lt;/strong&gt; is of course one of the solutions to get rid of technical debt, but it is usually unnecessary and &lt;a href="https://accesto.com/blog/5-reasons-why-rewriting-an-applications-from-scratch-is-a-bad-idea/"&gt;we discourage that approach&lt;/a&gt;. Although it significantly lowers technical debt, it &lt;strong&gt;freezes the product development for a long time&lt;/strong&gt; and competition and users won’t wait. &lt;/p&gt;

&lt;p&gt;Another approach is to continue the development of new features in the old codebase, but with a focus on refactoring. Creating a new module? Do it without taking any shortcuts and also improve parts of the application that relate to that module. This won’t freeze your development and &lt;strong&gt;will let you pay off the debt in smaller chunks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Working with clients at Accesto, we usually implement a solution that goes slightly further. Instead of developing new features in legacy code, we create a new, separate codebase for them. &lt;strong&gt;All the new features are implemented in that new part, and the old code is step by step migrated to that new part&lt;/strong&gt;. More on that approach, you can read in a blogpost of our CTO on &lt;a href="https://accesto.com/blog/handle-technical-debt-in-legacy-application-4-possible-scenarios/"&gt;possible scenarios of handling technical debt&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;During the last 5 years at Accesto, we focused mostly on helping successful web applications to &lt;strong&gt;get rid of technical debt accumulated by years of growth&lt;/strong&gt;. If you are not sure whether it is worth fighting debt in your software let me just mention two of our case studies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;One of our customers reached us when previous developers denied further work on his product, burdened by accumulated technical debt. The approach mentioned above turned into a &lt;strong&gt;35% yearly debt reduction&lt;/strong&gt;. Their &lt;a href="https://accesto.com/case-study/legacy/"&gt;legacy application evolved to highly scalable SaaS in just 2 years&lt;/a&gt;, with &lt;strong&gt;time to market for new features reduced by 87%&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And our recent story of &lt;a href="https://accesto.com/case-study/placker/"&gt;accelerating SaaS growth by gradual improvements in web architecture&lt;/a&gt;. After 1.5 years of cooperation with Accesto, our customer won the Atlassian’s Codegeist 2020 contest for the Best app for remote working. Thanks to the new architecture, &lt;strong&gt;the module that won the contest was created in just 3 weeks&lt;/strong&gt;. Something that would be impossible before.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am glad that, although a couple of years ago technical debt made us shut down our product, we are now helping others to avoid such bitter decisions. If you are also looking for help, just let me know, we can verify how much debt is hidden in your web app and suggest the best approach to get rid of it.&lt;/p&gt;

</description>
      <category>technicaldebt</category>
      <category>craftsmanship</category>
      <category>codequality</category>
    </item>
    <item>
      <title>What is Docker and why to use it?</title>
      <dc:creator>Piotr Gołofit</dc:creator>
      <pubDate>Thu, 10 Mar 2022 08:19:05 +0000</pubDate>
      <link>https://dev.to/accesto/what-is-docker-and-why-to-use-it-simple-explanation-448p</link>
      <guid>https://dev.to/accesto/what-is-docker-and-why-to-use-it-simple-explanation-448p</guid>
      <description>&lt;h2&gt;
  
  
  What is Docker and what are Docker containers?
&lt;/h2&gt;

&lt;p&gt;Let’s imagine you would like to build a website or a web application. What do you need to make your next Facebook, Youtube, or Twitter up and running? For sure you will need to develop some code, as it is a key part of every or web software. But the code on its own is not enough. Websites and applications consist of many additional parts, like databases, configuration files, runtime libraries, and other third-party software. &lt;strong&gt;Docker&lt;/strong&gt; is a tool that allows you to take all the parts and bundle them together into one package, called &lt;strong&gt;the container&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1xiN3-17--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pxgxbv5u9wuoi6d3zj0a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1xiN3-17--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pxgxbv5u9wuoi6d3zj0a.png" alt="Docker Container" width="880" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But why would you like to close your application in the box? Because you can then use Docker to launch this box on any server (including cloud) or a local machine (eg. developers computer) you want. And it will run everywhere in exactly the same, predictable way. With no additional configuration, installation, and adjustments required for each environment you run it. Trust me, that will save you a lot of trouble, effort, and in the end - money.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Non4xSFa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o3frsvqtaw2q9pm0e0jf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Non4xSFa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o3frsvqtaw2q9pm0e0jf.png" alt="Docker running in different enviroments" width="880" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not convinced? And what if I tell you that you can use Docker to run many such boxes at the same time? That becomes quite useful if you want to build a scalable website or web application that can handle many users. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UCbpPAOG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8oahlw59miyld82yvcxv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UCbpPAOG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8oahlw59miyld82yvcxv.png" alt="Scaling docker containers" width="880" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s see what else Docker can do for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the benefits of Docker? Is Docker worth using?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Easily introduce new developers to your project
&lt;/h3&gt;

&lt;p&gt;How quickly a new developer can start working on your product? Assuming he already has code on his machine, he still has to install some local server, set up a database, install required libraries, third party software, and configure it all together. How long does it take? From a few hours to many days. Even if your project has a good onboarding manual that explains all the steps, there is a high chance that it won’t work for every laptop and system configuration. Usually, onboarding a new developer is a collaborative effort of the whole team that helps the newcomer to install all missing parts and resolve all system issues.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_0uY0y-u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/shrjldx8huxmcgp7fih0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_0uY0y-u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/shrjldx8huxmcgp7fih0.png" alt="Docker compose project setup" width="880" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And with Docker? With Docker all these manual installation &amp;amp; configuration steps are done automatically inside the Docker container. So it is as simple as launching Docker and running one command (docker-compose up), that will do all the work for the developer. No matter if he uses &lt;a href="https://accesto.com/blog/docker-on-mac-how-to-speed-it-up/"&gt;Docker on macOS&lt;/a&gt;, Windows or Linux. &lt;/p&gt;

&lt;p&gt;This will requrie to create a Docker setup for the project first, but that is something you do only once and later everyone can benefit out of it ( see an example &lt;a href="https://accesto.com/blog/simple-docker-setup-for-symfony-project/"&gt;Docker setup for Symfony project&lt;/a&gt; ). &lt;/p&gt;

&lt;h3&gt;
  
  
  2. Make your app behave in a consistent and predictable way in all environments
&lt;/h3&gt;

&lt;p&gt;Ever heard of a famous developers’ excuse &lt;em&gt;“It worked on my machine”&lt;/em&gt;? The fact that something works on a developer's computer is not a guarantee that it will work on your server. There may be some library or a tool missing, or a mismatch in required versions of installed software. Each computer or server is different, and configured in a different way. Without Docker you have to set up each of the machines and system manually, and this is time consuming and error prone. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hth_wKJl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m29pxla59l8fdbh8xov2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hth_wKJl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m29pxla59l8fdbh8xov2.png" alt="Consistency with docker containers" width="880" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Docker solves this problem with bundling not only the code, but all the parts required to run the application or website. These parts are closed inside the Docker containers, which are isolated and independent from the outside world. And this makes them run in the same, consistent and predictable way everywhere. This means less time spent on fixing issues and more time spent on delivering new features.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Host your app wherever you want with no vendor locking
&lt;/h3&gt;

&lt;p&gt;Website or web application bundled into a Docker container is similar to a stuff packed inside a freight container. Docker containers, same as cargo ones, are standardized, so most of the computers, servers and cloud (as container ship, freight trains, trucks) can handle them. This gives you a great flexibility on the hosting technology and provider. &lt;/p&gt;

&lt;p&gt;You can start with a small and cheap server, and scale when needed into another machine or to the cloud. And what’s more, you won’t get stuck with one hosting vendor.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Flexible scaling of your app or website
&lt;/h3&gt;

&lt;p&gt;Although using Docker won’t make your website or web application scalable out of the box, it may be one of the key ingredients of your software scalability. Especially if you use some &lt;a href="https://accesto.com/blog/what-exactly-is-the-cloud/"&gt;cloud services&lt;/a&gt; like AWS or Google Cloud as your hosting solution. Docker containers, if built properly, can be launched in many copies to handle the growing number of users. And on cloud this scaling can be automated, so if more people use your website, more containers will be launched. Same is about downsizing, less users, smaller the bill for your hosting. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wKn6Sv7q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/whapmli1okthtfbqjpdi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wKn6Sv7q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/whapmli1okthtfbqjpdi.png" alt="Scaling docker with business" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Easily compose your application with third party software
&lt;/h3&gt;

&lt;p&gt;As mentioned before, Docker helps to bundle your code together with third party software inside closed containers. But it also provides a large repository (&lt;a href="https://hub.docker.com/" rel="nofollow"&gt;Docker Hub&lt;/a&gt;) of available templates (called Docker Images) that can be used by developers to create containers for your website or web application. This shortens the development process, because your development team, instead of starting from the scratch, can reuse the configuration already created by the docker community. &lt;/p&gt;

&lt;h3&gt;
  
  
  6. Track all the changes in your application components, not only code
&lt;/h3&gt;

&lt;p&gt;Docker allows developers to define the whole environment around the code in a programmable way. So no more written or spoken instructions have to be executed by the human factor. No manual installation or configuration is required. No more guessing, and no more misunderstandings when one of the developers installed some library but did not bother to inform others that from now on it will be required to launch the application. Docker converts all these manual processes into the code. Code that is saved, tracked, and that will execute automatically doing all the installation and configuration on all developers' computers, as well as on the servers. And that saves a lot of development time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://accesto.com/books/docker-deep-dive/"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bxWLSZMZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/czu8ipgw6c6xqd70pw3h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bxWLSZMZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/czu8ipgw6c6xqd70pw3h.png" alt="Docker Deep Dive Book" width="880" height="446"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;As you see, there are many benefits of using Docker. But simple adding it to your project won’t bring you any benefits just out of the box. It has to be used property. Our &lt;a href="https://accesto.com/books/docker-deep-dive/"&gt;free ebook&lt;/a&gt; on Docker optimizations and advanced techniques will definitely help with this. Make sure to recommend it to your development team. &lt;/p&gt;

&lt;h2&gt;
  
  
  How is docker different from Virtual Machines?
&lt;/h2&gt;

&lt;p&gt;If you are not familiar with virtual machines (VMs), just skip that section. But if you do, you may ask yourself the question: how does Docker differ from VMs? These may seem similar at the first look because both Docker Containers and Virtual Machines are kind of boxes in which you can put some code and mix it with additional software, files and configuration. &lt;/p&gt;

&lt;p&gt;But each virtual machine requires a separate operating system (like Windows or Linux) installed inside of it. And operating systems are not the lightest pieces of software. So if you have a couple of VM’s it may get quite heavy (both when it comes to the disk size and computing resources). Docker containers instead of having its own system, use the operating system of the computer/server on which Docker is installed. The whole magic of Docker engine is that it enables computer resources to the containers, as if each docker container had access to its own operating system. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sHlQ87xU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ugbk16y788tbv1ybd1a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sHlQ87xU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ugbk16y788tbv1ybd1a.png" alt="Docker container vs virtual machine" width="880" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In other words, each VM has its own cargo ship that carries it, while docker containers can be carried in bulk on one ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is Docker free for commercial use? Who is the owner of Docker?
&lt;/h2&gt;

&lt;p&gt;Docker was originally created by Solomon Hykes from dotCloud Inc. (later &lt;a href="https://www.docker.com/company" rel="nofollow"&gt;Docker Inc.&lt;/a&gt;) as an open source project and since it’s launch in 2013 Docker is available for free. In March 2017 the open source version of Docker was rebranded to Docker CE (Community Edition) and Docker Enterprise Edition (EE) was released.&lt;/p&gt;

&lt;p&gt;Docker CE is still completely free to use, also for commercial use, and for many web applications will be perfectly sufficient. Core features and functions of both CE and EE versions are the same, both are updated quarterly, and both are available on a wide range of popular hosting solutions including cloud infrastructures - which gives freedom of choice without lock-in. &lt;/p&gt;

&lt;p&gt;You may ask yourself, do I need Docker Enterprise Edition then? Docker EE has additional features for launching, managing and securing containers in a more efficient way. It is the same Docker as in CE, but surrounded with additional tools, services and with official support. This may simplify Docker management and maintenance if your application uses many different containers. &lt;/p&gt;

&lt;h2&gt;
  
  
  Can Docker make a pizza 🍕 for me?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;To better understand Docker, let me leave you with one tasty metaphor.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine you would like to open a pizza business. What do you need to start serving the best pizza menu (features) in town? First of all, you need an experienced chef (developer) to create pizza recipes (code) for you. These recipes (code) can be used to combine the ingredients (data) into a tasty pizza (application features).&lt;/p&gt;

&lt;p&gt;But to run an actual pizza place, you also need some infrastructure. Building (operating system), storage compartments (database), well stuffed kitchen (frameworks) and various appliances (third party libraries). And of course you need an employee (runtime environment) with some cooking skills (programing language) to bake your delicious pizza (application/website).&lt;/p&gt;

&lt;p&gt;And what if you want to open a chain of the restaurants? You can use the same pizza recipes, for sure. But to ensure the same pizza taste and quality you will have to carefully recreate all the infrastructure, resources and procedures as well. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KDnRgZ32--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ukhcwfxzipliapprl9a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KDnRgZ32--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ukhcwfxzipliapprl9a.png" alt="Docker container as pizza food truck" width="880" height="302"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here comes the Docker. It allows you to write down not only the recipe for a pizza, but for the whole pizza restaurant franchise. But with one difference - instead of a stuffed building, you will get more like a recipe for a pizza food truck, that can be easily copied and placed in any place you want. And it will serve there the same tasty pizza your chef has initially developed. Enjoy!&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker sounds like good solution for your project?
&lt;/h2&gt;

&lt;p&gt;Want your team of developers dive into a Docker world? Make sure to show them our &lt;a href="https://accesto.com/books/docker-deep-dive/"&gt;Docker Deep Dive&lt;/a&gt; free ebook on that topic. They will find inside good practices, tools and techniques that will help to make the most of Docker. &lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>containers</category>
    </item>
    <item>
      <title>When to use and when not to use Docker?</title>
      <dc:creator>Piotr Gołofit</dc:creator>
      <pubDate>Wed, 02 Mar 2022 10:32:03 +0000</pubDate>
      <link>https://dev.to/accesto/when-to-use-and-when-not-to-use-docker-2694</link>
      <guid>https://dev.to/accesto/when-to-use-and-when-not-to-use-docker-2694</guid>
      <description>&lt;p&gt;If you are reading this post, you probably already heard about Docker. You may be wondering, why is Docker so popular? Is it just another hype word, or is it really useful? Will it bring you any value in the long term? Or will be gone in the next few years like many other fancy tools? And finally, &lt;strong&gt;should your development team start using Docker?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this article, I will show you a few &lt;strong&gt;good reasons to use Docker&lt;/strong&gt;, and a couple of examples where it may not be the best option. But let’s start by clarifying, what Docker actually is?&lt;/p&gt;

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

&lt;p&gt;In a nutshell, Docker is a tool that allows developers to &lt;strong&gt;bundle all the ingredients of software they create into unified boxes&lt;/strong&gt; - called Docker containers. Key ingredient is usually the code, but most apps or websites won’t work without a database, configuration files, runtime libraries or other third-party software. Putting these together in a box makes it &lt;strong&gt;easier to launch the app on another developer's computer or the server&lt;/strong&gt;. And because Docker containers are unified and independent from the outside world, the software built with them will work in a consistent, predictable way on each machine. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GCpGzLYt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2z44jp67g1ugf5r7800b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GCpGzLYt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2z44jp67g1ugf5r7800b.png" alt="Docker logo Accesto" width="880" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to understand it better, make sure to check my previous blog post on &lt;a href="https://accesto.com/blog/what-is-docker-and-why-to-use-it/"&gt;What is Docker?&lt;/a&gt;. Knowing the basics, let’s see when you should consider using Docker in your project, and when not. &lt;/p&gt;

&lt;h2&gt;
  
  
  Good reasons to use Docker
&lt;/h2&gt;

&lt;p&gt;From my experience, you should &lt;strong&gt;consider using Docker in your project if:&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Your development team is not set in stone
&lt;/h3&gt;

&lt;p&gt;How often do your development team change? Introducing new developers to the project always takes some time. Before they can start coding they have to set up their local development environment for the project - eg. some local server, database, or third party libraries. This may take from a few hours to many days, depending on the project complexity and how well is the project setup manual. Docker automates this setup &amp;amp; installation work so new developers can be productive from the first day. Instead of doing everything manually, they simply run one command that will prepare the development environment for them. This saves a lot of time, and the bigger is your development team and higher rotation, the more you will gain by using Docker. &lt;/p&gt;

&lt;h3&gt;
  
  
  2. Your software runs in different environments
&lt;/h3&gt;

&lt;p&gt;I assume your software runs in at least two different environments - developers computer, and some server. Even in such a simple case, you can notice inconsistent behavior of your app between the machines it runs on. Something that worked on a developer's computer may not work on the server. And the number of issues grows with every new environment - like another server (test server vs production server) or simply another developer’s computer with a different operating system. Docker allows you to run the software in containers separated from the outside world, so your app can work in a consistent, predictable way in every environment. &lt;/p&gt;

&lt;h3&gt;
  
  
  3. Your software grows and consists of many bits and pieces
&lt;/h3&gt;

&lt;p&gt;Developers add new libraries, services, and other dependencies to the software daily. The more complex becomes your software the harder it is to keep track on all its parts that are required for it to run. Without Docker, all the changes in the project setup have to be communicated to other developers and documented. Otherwise, their version of the code will stop working, and they won’t know why. With Docker, all the required components of the software are specified in Docker configuration files (like Dockerfile and docker-compose.yml). And if something is specified to this configuration it is automatically added for every developer working on the project. &lt;/p&gt;

&lt;h3&gt;
  
  
  4. You want your software to be scalable and handle more users
&lt;/h3&gt;

&lt;p&gt;Want to scale your application to new markets or branches? Great! But will it handle the &lt;a href="https://accesto.com/blog/why-new-users-can-kill-your-web-application/"&gt;growing number of users&lt;/a&gt;? Docker won’t automatically make your app scalable but can help with this. Docker containers can be launched in many copies that may run in parallel. So the more users you have, the more containers you launch (eg. in the cloud). Before you do so, your software has to be prepared for running multiple instances at the same time. But if the scalability blockers are already removed, Docker containers will come in very handy to launch a scalable application. &lt;/p&gt;

&lt;h3&gt;
  
  
  5. Your hosting infrastructure may change in the future and you don’t want to be locked
&lt;/h3&gt;

&lt;p&gt;Small websites and applications don’t require complex hosting infrastructure. But when a business grows and evolves, so do the server requirements. In the fast-paced business environment, web infrastructure needs to be flexible enough to adapt quickly. Both to ensure that your &lt;a href="https://accesto.com/blog/making-sure-the-website-wont-crash-again/"&gt;website won’t crash&lt;/a&gt; and that the costs of the infrastructure correspond to the actual needs. And because Docker containers are unified and very well adopted, they can be launched in almost any server environment. So when your needs change, your software can be placed somewhere else. So you won’t be locked by any hosting vendor or a specific type of infrastructure. &lt;/p&gt;

&lt;h3&gt;
  
  
  6. You do some proof of concepts and test new technologies for your software
&lt;/h3&gt;

&lt;p&gt;Docker is quite useful if you want to experiment with different technologies. Want to adopt some new database, programming language, or other tools? Most probably there is already a Docker template for this (called Docker Image) created by a docker community. &lt;a href="https://hub.docker.com/" rel="nofollow"&gt;Docker Hub&lt;/a&gt; is a large repository of ready docker images for almost any technology you want to use. Just find the image you want and add it to your Docker configuration. No need for time-consuming installation or debugging compatibility issues. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CwXkCP5C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/41kt8x8gr8d9u4fkb5fi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CwXkCP5C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/41kt8x8gr8d9u4fkb5fi.png" alt="When to use Docker" width="824" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If at least some of the above statements are true for your projects, you should probably &lt;strong&gt;give Docker a try&lt;/strong&gt;. It can be useful for the majority of web development projects, but there are also some cases when it will not bring a lot of value. Let’s see some of them. &lt;/p&gt;

&lt;h2&gt;
  
  
  When to avoid Docker?
&lt;/h2&gt;

&lt;p&gt;None of the below cases (except maybe the last one) automatically make Docker useless for your project. But if you hit any of these, you should rethink if Docker will be the best solution for your software development. &lt;strong&gt;Better think twice before adopting Docker if:&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Your software product is a desktop application
&lt;/h3&gt;

&lt;p&gt;Docker is very useful for web applications running on a server or console-based software. But if your product is a standard desktop application, especially with a rich GUI, Docker may not be the best choice. Although it is technically possible to build such an app with Docker, it isn’t the natural environment for running software with a graphical interface and it requires additional workarounds. &lt;/p&gt;

&lt;h3&gt;
  
  
  2. Your project is relatively small and simple
&lt;/h3&gt;

&lt;p&gt;Docker is really handy if your software consists of multiple parts. It makes it easier to install these and keep a track of all the dependencies. But it does not come like this out of the box. Someone has to prepare the initial project setup for Docker (Dockerfiles, docker-compose.yml, entry points, etc.) and maintain it in the future. So if your app is relatively simple and does not require any additional tools or services, you may start without Docker. It can be added later on when your software grows. &lt;/p&gt;

&lt;h3&gt;
  
  
  3. Your development team consists of one developer
&lt;/h3&gt;

&lt;p&gt;If your development team is simply a one-man-army, the benefits of Docker will be smaller. Docker helps to ensure that all the developers have access to all the necessary bits and pieces of the software they work on. So if someone adds software dependencies, everyone has them when needed. If it is just one developer, there is no such need. But even in this case, Docker may help, eg. if that person resigns and someone has to take over the project. But that can be handled with proper documentation instead. Docker simply automates this, but the fewer people in the team, the smaller the need for automation. &lt;/p&gt;

&lt;h3&gt;
  
  
  4. You are looking for a solution to speed up your application
&lt;/h3&gt;

&lt;p&gt;Docker may speed up your development process significantly, but not necessarily your app itself. Although it helps with making your application scalable, so more users will be able to use it, the single instance of your app will usually be just a hint slower than without Docker. Fortunately, Docker containers are smaller than for example Virtual Machines and require much fewer resources. In most cases, the performance overhead of Docker won’t be visible for you, but if your goal is to boost the speed of your app, Docker itself is not the solution for that.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Your development team consist mostly of MacBook users
&lt;/h3&gt;

&lt;p&gt;Speaking of speed, Docker has serious performance issues when running on macOS. These are related to how volumes are mounted, and the underlying osxfs filesystem. In short, if your app performs a lot of read/write disk operations (and almost every app does some) it may be very, very slow on Mac. So if your development team consists of Apple fans, Docker may not be the best solution. Fortunately, there are a few things that MacBook users can do to improve their experience with Docker. Our developer Krzysiek summarized them in his recent post - &lt;a href="https://accesto.com/blog/docker-on-mac-how-to-speed-it-up/"&gt;Docker on Mac - how to speed it up?&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  6. Your team don’t know how to use Docker properly
&lt;/h3&gt;

&lt;p&gt;Last, and maybe most important case when you should not use Docker. Docker can work like charm and significantly speed up the development process. But as well may become your nightmare, if it is not used properly. Very large Docker images that start up for many minutes. Difficult to debug issues that do not produce any useful logs. Security issues when using random, third-party Docker images. Developers that install dependencies on their machine and not in the Docker images. Docker configuration mixed with additional manual commands to be executed. These all can cause a lot of frustration, issues and in the end, may cost real money. So if your development team doesn't know &lt;a href="https://accesto.com/books/docker-deep-dive/"&gt;how to use Docker properly&lt;/a&gt;, don’t use it just because everyone does. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LFirgwcG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fxnm31wp56gj0lylsl18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LFirgwcG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fxnm31wp56gj0lylsl18.png" alt="When not to use Docker" width="824" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  So should my team use Docker?
&lt;/h2&gt;

&lt;p&gt;Well, in most cases, yes. Docker, when used properly, can be beneficial quite quickly. And will work for most of the software products. Containerization, in general, is the &lt;strong&gt;natural next step in the software development industry&lt;/strong&gt; and won’t disappear anytime soon. Docker may be replaced by other tools or next versions of Docker, but the general concept will remain. But as with every tool, &lt;strong&gt;Docker won’t help you if it is not used properly&lt;/strong&gt;. So before your development team starts to complain about Docker, let them read our free ebook &lt;a href="https://accesto.com/books/docker-deep-dive/"&gt;Docker Deep Dive&lt;/a&gt; - they will thank you later. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://accesto.com/books/docker-deep-dive/"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xkt83cqp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/owl7cdegri0665grzjz6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xkt83cqp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/owl7cdegri0665grzjz6.png" alt="Docker Deep Dive Book" width="880" height="446"&gt;&lt;/a&gt;&lt;/p&gt;



</description>
      <category>docker</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
