<?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: Vivek Kodira</title>
    <description>The latest articles on DEV Community by Vivek Kodira (@vivekkodira).</description>
    <link>https://dev.to/vivekkodira</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%2F44510%2F8ce07b0b-67df-4fc3-9ace-a15138194e00.jpeg</url>
      <title>DEV Community: Vivek Kodira</title>
      <link>https://dev.to/vivekkodira</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vivekkodira"/>
    <language>en</language>
    <item>
      <title>Choosing an AI IDE</title>
      <dc:creator>Vivek Kodira</dc:creator>
      <pubDate>Tue, 01 Jul 2025 18:02:59 +0000</pubDate>
      <link>https://dev.to/vivekkodira/choosing-an-ai-ide-321o</link>
      <guid>https://dev.to/vivekkodira/choosing-an-ai-ide-321o</guid>
      <description>&lt;p&gt;NOTE: This article is cross-posted on my &lt;a href="https://kodira.in/blog/2025/07/01/choosing-ai-ide" rel="noopener noreferrer"&gt;blog&lt;/a&gt;. It will be kept updated there.&lt;/p&gt;

&lt;p&gt;Earlier this month, I had the opportunity to review several AI IDEs. I was asked to recommend an AI assistant for an engineering team. This post is a summary of my approach &amp;amp; findings. You probably will not learn anything earth shattering but I hope you find it useful to help clarify your own thought processes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Criteria: Cost
&lt;/h2&gt;

&lt;p&gt;I dismissed cost early on in the exercise. Most of the assistants are comparable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 Compare AI Models
&lt;/h2&gt;

&lt;p&gt;I started by comparing models across different criteria &amp;amp; languages using sites like &lt;a href="https://www.prollm.ai/" rel="noopener noreferrer"&gt;https://www.prollm.ai/&lt;/a&gt;.&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%2Fleezblys7gxy440qtxvn.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%2Fleezblys7gxy440qtxvn.png" alt="Image description" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, for my chosen stack, Claude Sonnet was the winner. But the others were so close that the comparison was meaningless. For my needs, a 2 decimal difference was as good as no difference at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Ask AI
&lt;/h2&gt;

&lt;p&gt;Then, I asked Google Gemini make me a report. My prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Come up with the criteria an AI IDE should have. Assign weights to each and then use them to compare the IDEs available today. Support for  should get special mention. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Gemini produced a comprehensive report and this was more useful. It rated &lt;a href="https://www.cursor.com/" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/features/copilot" rel="noopener noreferrer"&gt;Github Copilot&lt;/a&gt; &amp;amp; provided articles as reference. &lt;/p&gt;

&lt;p&gt;The problem: many of the articles it used as reference were paid ones: marketing material from the companies themselves. &lt;/p&gt;

&lt;p&gt;I repeated the exercise asking Gemini to ignore such sources &amp;amp; Cursor &amp;amp; Copilot still stayed on top.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Manually verify
&lt;/h2&gt;

&lt;p&gt;At about this time, I was asked to include Amazon Q in the comparison. &lt;/p&gt;

&lt;p&gt;I decided to do a comparison for myself. For my tests, I chose Windsurf, Copilot, Cursor, TabNine &amp;amp; Amazon Q&lt;/p&gt;

&lt;p&gt;My testing involved the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gave it a simple React component as context &amp;amp; asked it to run the unit test&lt;/li&gt;
&lt;li&gt;Gave it a JSP and asked it to explain the code to me &amp;amp; list all the APIs the JSP invoked&lt;/li&gt;
&lt;li&gt;The JSP had several security issues I was already aware of. I asked each AI assistant to review the code &amp;amp; list all security issues it could find&lt;/li&gt;
&lt;li&gt;Finally, I asked each AI assistant how it would redesign the usecase: what APIs would it add, modify or delete.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The results have been summarised below. Copilot &amp;amp; Cursor were again the best of the lot. Next came Windsurf, then Amazon. I was quite disappointed with TabNine's output and dropped it at this stage.&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%2F7tinsa4em2l1t1c4ftqf.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%2F7tinsa4em2l1t1c4ftqf.png" alt="Image description" width="800" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, I had two close contenders &amp;amp; needed to make a choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4. Revelations
&lt;/h2&gt;

&lt;p&gt;While this exercise was happening, I was also using AI for my own work &amp;amp; observing how members of my team used AI for their work.&lt;/p&gt;

&lt;p&gt;This was when I had two revelations&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Most developers I saw weren't using the assistants well at all.&lt;/li&gt;
&lt;li&gt; How well an AI assistant performed at one task wasn't enough. I also need to ask: How easy was it to use as a beginner? How much friction did it cause? How easy was it to learn to use it well? &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Revelation 1: Developers are the bottleneck
&lt;/h3&gt;

&lt;p&gt;One example: Several developers were copying code snippets &amp;amp; pasting them in ChatGPT on the browser and asking it to solve the issue providing very terse &amp;amp; obscure instructions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;error here&lt;/code&gt; UserDAO is not finding session_id. What do I do? &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;( &lt;em&gt;What should the user have done?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This kind of question needed knowledge of the codebase which an IDE-based assistant can provide better&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;To its credit, ChatGPT did a great job assuming the context and provided somewhat valid suggestions&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Another example: Giving the AI a too vague or too broad instruction. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Run &lt;code&gt;npm run eslint&lt;/code&gt;. Fix all the errors you find.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Why is this a problem?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI assistants work best when they are provided enough context &amp;amp; clear rules. Most times when such broad instructions were given, the AI struggled with reading the output of the command prioritising what it should do. The user would invariably find more issues than they started with or the AI would go into a loop vomitting unnecessary code at the problems&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One part of the problem here are developers themselves. They'd not invested the time &amp;amp; efforts necessary to experiment &amp;amp; learn how to use AI well. This could be solved by training developers on Prompt engineering. Another part was related to the 2nd revelation - how well designed was the assistant?&lt;/p&gt;

&lt;h3&gt;
  
  
  Revelation 2: AI Assistant Maturity
&lt;/h3&gt;

&lt;p&gt;Is the AI assistant easy to use? Does it make the user better at using it over time? &lt;/p&gt;

&lt;p&gt;This finally was the differentiator I was looking for and was the point where Copilot fell down the rankings to 2.&lt;/p&gt;

&lt;p&gt;Cursor, Windsurf and others like it are iterating quickly. They introduced features like memories, rules which have made us developers better at using AI for our work. Copilot is catching up but is still behind. &lt;/p&gt;

&lt;p&gt;Here are three simple but critical examples:&lt;/p&gt;

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

&lt;p&gt;This is how &amp;amp; what Cursor allows you to add to a chat context&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%2Fuzvawihzmbqtxtde08pq.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%2Fuzvawihzmbqtxtde08pq.png" alt="Image description" width="646" height="646"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that you stay where you are &amp;amp; can add code, folders, rules, terminals and a lot more.&lt;/p&gt;

&lt;p&gt;Now this is what Copilot does when you click on Add context in a chat:&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%2F8wsfdjwih2v1ujdwgs64.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%2F8wsfdjwih2v1ujdwgs64.png" alt="Image description" width="800" height="611"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the button and you jump from the context window to somewhere else. Where you can &lt;strong&gt;&lt;em&gt;only&lt;/em&gt;&lt;/strong&gt; add code. And sometimes even after you've added a context &amp;amp; are conversing with the AI, it randomly forgets the context in favour of the file you've currently opened. To add instructions (copilot's equivalent of rules), you navigate to a completely different location&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%2Fhpotvd1huqqfwgyi97xk.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%2Fhpotvd1huqqfwgyi97xk.png" alt="Image description" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;How I normally use an AI assistant these days is to help me improve the overall quality of a codebase. I experiment &amp;amp; iteratively improve a prompt to a point where the AI is doing exactly what I want it to do. I then ask it to go over the codebase &amp;amp; apply simliar changes to all the targets it finds. Then I go have a coffee. When I come back, the whole codebase is cleaned up &amp;amp; any workflow I've defined afterwards (ex: lint, tests) have been run. &lt;/p&gt;

&lt;p&gt;Dedicated IDEs like Windsurg &amp;amp; cursor does this beautifully. &lt;/p&gt;

&lt;p&gt;For instance, in Cursor, the user is able to setup &amp;amp; control every aspect of "auto-run". This feature has been present for months!&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%2F67w2rtjf3043xtq349l5.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%2F67w2rtjf3043xtq349l5.png" alt="Image description" width="800" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In contrast, there has been no such option yet in Copilot. It has been a pain to keep clicking "Continue" every few minutes in Copilot (&lt;em&gt;Coincidentally, Copilot may have got this feature today (30th June, 2025) &lt;a href="https://github.com/microsoft/vscode/issues/252496#issuecomment-3023801897" rel="noopener noreferrer"&gt;Github issue&lt;/a&gt;&lt;/em&gt;)&lt;/p&gt;

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

&lt;p&gt;Sometimes I forget my own rules of keeping AI conversations short &amp;amp; the AI ends up doing something I need to revert. Cursor allows me to go back in history quite easily.&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%2F6c2g5w35nvm3zxnzjjln.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%2F6c2g5w35nvm3zxnzjjln.png" alt="Image description" width="646" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copilot does not support this feature as of today. &lt;a href="https://github.com/microsoft/vscode-copilot-release/issues/9391" rel="noopener noreferrer"&gt;Github Issue&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are a few more such examples and I'm sure Copilot will catch up. But as of today, IDEs like Windsurf &amp;amp; Cursor do a much better job than Copilot.&lt;/p&gt;

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

&lt;p&gt;So, what AI assistant should you pay &amp;amp; buy an annual subscription for? &lt;/p&gt;

&lt;p&gt;Answer: None of them. Instead, buy monthly subscriptions &amp;amp; experiment with any popular AI based assistant. All of them are iterating &amp;amp; improving. At this point, tying yourself to any one is a bad idea. &lt;/p&gt;

&lt;h2&gt;
  
  
  My recommendations
&lt;/h2&gt;

&lt;p&gt;For now, I've chosen these tools as my current workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replit for quick prototyping&lt;/li&gt;
&lt;li&gt;Cursor for productionising&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>cursor</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>Logseq for Task Management</title>
      <dc:creator>Vivek Kodira</dc:creator>
      <pubDate>Sun, 16 Apr 2023 17:56:20 +0000</pubDate>
      <link>https://dev.to/vivekkodira/logseq-for-task-management-3d4d</link>
      <guid>https://dev.to/vivekkodira/logseq-for-task-management-3d4d</guid>
      <description>&lt;p&gt;&lt;strong&gt;NOTE: The updated version of this article is cross-posted &lt;a href="https://kodira.in/blog/2023/09/16/logseq-for-task-management" rel="noopener noreferrer"&gt;on my website&lt;/a&gt;. I'm observing some issues with dev.to where images suddenly don't appear and so will not be maintaining this article going forward.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hello!,&lt;/p&gt;

&lt;p&gt;I've been using &lt;a href="https://logseq.com/" rel="noopener noreferrer"&gt;Logseq&lt;/a&gt; for Task Management for the past several months and thought I'd write a post about the features I use and love about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this post is
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It is an incomplete list of features I think a good Task Management system should support with examples of how Logseq does them. Where possible, I include a few tips.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What this post is not
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It is not about the benefits of personal Knowledge or Task Management .&lt;/li&gt;
&lt;li&gt;It is not a comprehensive list of what features a Task Management System should have.&lt;/li&gt;
&lt;li&gt;It is not an introduction to Logseq&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Intended audience
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This post is for people who are already sold on the merits of Knowledge &amp;amp; Task Management. And have tried tools like Notion, Todoist, Obsidian, Roam Research etc. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I too have tried these tools and switched to Logseq because of the following reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is open source&lt;/li&gt;
&lt;li&gt;Its license is not restrictive and allows me to use it for Personal projects and at work&lt;/li&gt;
&lt;li&gt;Its content is stored as markdown so my notes are not in some proprietary format that I cannot later decipher&lt;/li&gt;
&lt;li&gt;The community around the tool is kind (and active)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to read this post
&lt;/h2&gt;

&lt;p&gt;In most cases, I've used a little text to introduce a GIF. The GIFs serve as a visual guide or example of the feature I'm demonstrating. &lt;/p&gt;

&lt;h2&gt;
  
  
  References/Credits
&lt;/h2&gt;

&lt;p&gt;Almost everything I've learnt about Logseq has been from &lt;a href="https://twitter.com/OneStuttering" rel="noopener noreferrer"&gt;OneStuttering&lt;/a&gt;. His &lt;a href="https://www.youtube.com/@OneStutteringMind/featured" rel="noopener noreferrer"&gt;videos&lt;/a&gt; are a treasure trove of information&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Easy Learning Curve
&lt;/h3&gt;

&lt;p&gt;A Task Management System should be easy to use. You should discover new features as you mature. &lt;/p&gt;

&lt;p&gt;Logseq does this well. It starts with a journal view. Adding Tasks is as trivial as creating a simple list. As you learn more about the tool and task management, you'll find yourself thinking: "I wish I could do this" and discover that Logseq supports it.&lt;/p&gt;

&lt;p&gt;Pro Tip: Start small. Don't try to build a system overnight. Doing so will make both adopting the tool harder and take you down a rabbithole of customisations you don't need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create and Manage Tasks
&lt;/h3&gt;

&lt;p&gt;At its most basic level, a Task Management System should allow you to create and manage tasks (i.e. set statuses). &lt;/p&gt;

&lt;p&gt;In Logseq, the blocks you add can become tasks with just a couple of key strokes &lt;code&gt;CTRL+ENTER&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Pro Tips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change the settings to switch to the workflow you prefer&lt;/li&gt;
&lt;li&gt;Install the "Task Management Shortcuts" plugin - it allows you a few more statuses such as CANCELLED and WAITING&lt;/li&gt;
&lt;li&gt;There are other very opinionated plugins if you want to try them. Explore the marketplace!&lt;/li&gt;
&lt;/ul&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%2F3nh01u679gjz3yfn81nn.gif" 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%2F3nh01u679gjz3yfn81nn.gif" alt="Create Tasks" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Prioritise Tasks
&lt;/h3&gt;

&lt;p&gt;You should be able to prioritise tasks so that you can do the important stuff first. &lt;/p&gt;

&lt;p&gt;Logseq ships with some default priorities: &lt;code&gt;/A&lt;/code&gt;, &lt;code&gt;/B&lt;/code&gt; or &lt;code&gt;/C&lt;/code&gt; . Adding your own custom priorities is trivial - at the end of the day, these are just links to a page.&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%2Fniciqsfloy98vpiae9d8.gif" 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%2Fniciqsfloy98vpiae9d8.gif" alt="Prioritise tasks" width="1438" height="872"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Schedule tasks
&lt;/h3&gt;

&lt;p&gt;You should be able to schedule tasks to another day.&lt;/p&gt;

&lt;p&gt;LogSeq allows you to do this using &lt;code&gt;/schedule&lt;/code&gt; - you can pick the date and time when you want the task to surface and it will show up on that day in the journal&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%2Fdogj49uykkexc2ovz6we.gif" 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%2Fdogj49uykkexc2ovz6we.gif" alt="Scheduling Tasks" width="1436" height="874"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Repeatable Tasks
&lt;/h3&gt;

&lt;p&gt;You will want to schedule some tasks to repeat every day or week. &lt;/p&gt;

&lt;p&gt;While Logseq's scheduling capabilities are simple, they are more than sufficient for my needs.&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%2Fv5bj6rekvot8ybrqv093.gif" 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%2Fv5bj6rekvot8ybrqv093.gif" alt="Schedule Repeatable tasks" width="1434" height="872"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ProTip: Use annual repeating tasks within a person's page to add birthday and anniversary reminders.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tagging and Projects
&lt;/h3&gt;

&lt;p&gt;To avoid navigating huge lists, being able to categorise tasks is important. Most task management systems support this feature but put it behind a paywall or worse, a subscription.&lt;/p&gt;

&lt;p&gt;Since Logseq automatically creates links to pages, these can then be used to categorise tasks. So you can now categorise tasks by tagging them with project names and then put references, links etc. in the project's pages&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%2Fuf4ckktqeilgxe7m31bw.gif" 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%2Fuf4ckktqeilgxe7m31bw.gif" alt="Categorize tasks" width="1433" height="872"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pro Tip 1: ALWAYS categorise tasks even if the project doesn't exist. You can always add details about the project later. Avoid generic categorisations like &lt;code&gt;misc, general&lt;/code&gt; as much as possible. &lt;/p&gt;

&lt;p&gt;Pro Tip 2: Add a "trigger" link to each task which will take you to the trigger for this task - could be a JIRA ticket, email or IM message. Will be useful when you are trying to trace a task back to why you did it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assigning Tasks
&lt;/h3&gt;

&lt;p&gt;You should be able to assign tasks to others and track their progress.  &lt;/p&gt;

&lt;p&gt;Almost every system today forces you to add users to accomplish this. In my case, that is never what I actually need. When I say others, I don't mean that I want other users to see the tasks I've assigned them. Only that my dashboard should allow me to add tasks and indicate that others are working on them.&lt;/p&gt;

&lt;p&gt;In Logseq, a person is just a block or a page like any other. So tagging a task as being done by another is trivial. &lt;/p&gt;

&lt;p&gt;Pro tip: Write the task in plain english as &lt;code&gt;#X to do Y&lt;/code&gt; rather than &lt;code&gt;do Y #x&lt;/code&gt; - the first is much more readable and will make sense months down the line&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%2Ft7unaypopbfr8dfn4xfs.gif" 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%2Ft7unaypopbfr8dfn4xfs.gif" alt="Assign users" width="1433" height="869"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Custom Dashboards
&lt;/h3&gt;

&lt;p&gt;Most Task Management Systems are strongly opinionated. &lt;/p&gt;

&lt;p&gt;Some Knowledge Management systems on the other hand, allow you too much customisation: so much so that you end up wasting hours on tools like Notion tweaking how it looks. Logseq's powerful query system allows you to build dashboards that will surface the content you are interested in. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;The embedded gif is sometimes not working. Here is a direct &lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3qd1lz5d84i1010gocvw.gif" rel="noopener noreferrer"&gt;link&lt;/a&gt; to the image&lt;/em&gt;&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%2F3qd1lz5d84i1010gocvw.gif" 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%2F3qd1lz5d84i1010gocvw.gif" alt="Create Dashboard" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In the GIF above, I first add a &lt;code&gt;/query&lt;/code&gt; to list all TODOs linked to my project. Then, realising a linked block is not a TODO, I go update it and verify that it shows up as well&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Pro Tip 1: People are just Pages so adding the same query to a person will give you a person-specific dashboard of tasks you've delegated to someone&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%2Fd1ckh9u86vyf8j0dca22.gif" 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%2Fd1ckh9u86vyf8j0dca22.gif" alt="Person Dashboard" width="1436" height="870"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pro Tip 2: I prefer a table like display for dashboards but the width available is not enough. To get rid of the spaces on either side, press &lt;code&gt;t+w&lt;/code&gt; - this narrows the margin on either side. If this doesn't help, install a theme or play around with the custom.css file in the root folder of your Logseq graph to get the look-and-feel you want.&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%2Fn5nrwgi6u6w5gzxvjilw.gif" 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%2Fn5nrwgi6u6w5gzxvjilw.gif" alt="Dashboard table" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Copy and Move Tasks
&lt;/h3&gt;

&lt;p&gt;You should be able to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copy a task to another day&lt;/li&gt;
&lt;li&gt;Move it to another day&lt;/li&gt;
&lt;li&gt;Postpone to another day&lt;/li&gt;
&lt;li&gt;Carry-over to another day&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This, in my mind, is one of its most powerful features in Logseq that allows me to use it as a Task Management tool&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copy: Select the text of the task and copy it  over to the target&lt;/li&gt;
&lt;li&gt;Move: Select the text. Cut and paste it in the target&lt;/li&gt;
&lt;li&gt;Postpone: First, create a section called "Postponed" in the  journal of the day you are postponing from, now copy the reference to the task (Click on the task but don't select any text and then press &lt;code&gt;CTRL+C&lt;/code&gt;) and paste the reference in the new day&lt;/li&gt;
&lt;li&gt;Carry-over: The same as above but in the original day you don't move the task to the "Postponed" section&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;The embedded gif is sometimes not working. Here is a direct &lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/um5y7ppykg04fz3nb8c1.gif" rel="noopener noreferrer"&gt;link&lt;/a&gt; to the image&lt;/em&gt;&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%2Fum5y7ppykg04fz3nb8c1.gif" 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%2Fum5y7ppykg04fz3nb8c1.gif" alt="Copy Move Postpone &amp;amp; carry over Task" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTE&lt;/em&gt; : Anything you add under a copied reference won't show up as a linked reference in the original topic. So you may want to add explicit links&lt;/p&gt;

&lt;p&gt;Pro Tip: 90% of the time, my tasks remain in the journal and are available in projects through Logseq's linking or my dashboards. Sometimes however, I "move" the task from the journal to the project. Doing so is again trivial. Copy the text over and add a link to the journal instead.&lt;/p&gt;

&lt;p&gt;I typically do this when I want to breakdown/plan a large project and want a single canvas to plan in. &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%2Fhi963876xe2usr8f2a7b.gif" 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%2Fhi963876xe2usr8f2a7b.gif" alt="Image description" width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Breakdown Tasks
&lt;/h3&gt;

&lt;p&gt;You should be able to breakdown a task. Indefinitely.&lt;/p&gt;

&lt;p&gt;This is again where most Task Management Systems I've used in the past don't match up to Logseq. Logseq's blocks-based approach allows you to add children indefinitely.&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%2Fet16emdtsdqhh06oofug.gif" 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%2Fet16emdtsdqhh06oofug.gif" alt="Break down Tasks" width="1434" height="867"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Narrow or Broaden focus
&lt;/h3&gt;

&lt;p&gt;Seeing a huge list of tasks is sometimes distracting. You should be able to narrow or broaden your focus as you wish. &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%2Fd4iompemez5okfp5sswd.gif" 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%2Fd4iompemez5okfp5sswd.gif" alt="Image description" width="1436" height="872"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Mobile Support
&lt;/h3&gt;

&lt;p&gt;Task Management systems should allow you to add tasks when you think of them. This means Mobile support and the ability to sync.&lt;/p&gt;

&lt;p&gt;ProTip: Logseq has a paid feature which allows you to sync between devices. If you'd rather not pay, you could use one of two hacks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add your graph to a git repository and sync using Github as source&lt;/li&gt;
&lt;li&gt;Move your graph to Google Drive and sync using Drive's own syncing feature. For Android, I found the app "DriveSync" very easy to use and you only need to pay a one-time fee instead of a subscription for the pro version.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Features it doesn't have
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Chaining Tasks
&lt;/h3&gt;

&lt;p&gt;"Getting Things Done" is the book my Task Management is most inspired from. The first tool that checked almost every box was &lt;a href="https://mgsd.tiddlyspot.com/#mGSD" rel="noopener noreferrer"&gt;mGSD&lt;/a&gt; - one feature it had that Logseq does not is the ability to create a chained sequence of tasks. Tasks with dependencies which need to be accomplished first are greyed out and when the dependency is marked completed, the next task becomes active. This &lt;a href="https://discuss.logseq.com/t/dependencies-task/738" rel="noopener noreferrer"&gt;feature is being discussed&lt;/a&gt; but is not available yet&lt;/p&gt;

&lt;h2&gt;
  
  
  Risks
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Logseq is opinionated about blocks. So there are Bullets everywhere!! - Thinking in this way can, at first, be challenging and some may never be able to do it.
&lt;/li&gt;
&lt;li&gt;Logseq stores everything in one flat folder structure. While you can create hierarchies in Logseq, the underlying files will all be at one level. This can also take some getting used to and has been &lt;a href="https://discuss.logseq.com/t/feature-request-creation-of-folders-and-subfolders-will-make-knowledge-management-more-feasible/3446/8" rel="noopener noreferrer"&gt;highlighted&lt;/a&gt; &lt;a href="https://discuss.logseq.com/t/proposal-changing-how-namespaces-function-in-logseq/3727" rel="noopener noreferrer"&gt;by many&lt;/a&gt; as a concern. This decision also makes it difficult to work on the files directly outside of Logseq - like say copying all the content about a Project someplace else. If this worries you, avoid Logseq for now. &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>logseq</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Speeding up Cypress Automation Tests</title>
      <dc:creator>Vivek Kodira</dc:creator>
      <pubDate>Sat, 18 Sep 2021 15:56:36 +0000</pubDate>
      <link>https://dev.to/feldspartech/speeding-up-cypress-automation-tests-e14</link>
      <guid>https://dev.to/feldspartech/speeding-up-cypress-automation-tests-e14</guid>
      <description>&lt;p&gt;Here are a few tips on making your &lt;a href="https://docs.cypress.io/guides/overview/why-cypress" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt; automation tests runs faster. &lt;/p&gt;

&lt;p&gt;NOTE: Some of these may seem trivial but they are all actual mistakes made by me. I hope you'll learn from my mistakes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write one big test rather than several small ones
&lt;/h3&gt;

&lt;p&gt;A real-world integration test typically involves signon, etc before testing the actual functionality. Each test you add will therefore add to the time taken. Also, as Cypress's best practices document &lt;a href="https://docs.cypress.io/guides/references/best-practices#Creating-tiny-tests-with-a-single-assertion" rel="noopener noreferrer"&gt;explains&lt;/a&gt;, Cypress does some housekeeping between each test. This will also slow you down if there are too many small tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use &lt;code&gt;before&lt;/code&gt; &amp;amp; &lt;code&gt;beforeEach&lt;/code&gt; judiciously.
&lt;/h3&gt;

&lt;p&gt;Consider this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[.homePage]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&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="c1"&gt;//10 tests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the test will login 10 times - once before each test. What I actually wanted was to login once and so should replace &lt;code&gt;beforeEach&lt;/code&gt; with &lt;code&gt;before&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nf"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[.homePage]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be.visible&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="c1"&gt;//10 tests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Avoid waiting for arbitrary periods of time.
&lt;/h3&gt;

&lt;p&gt;Cypress explains that &lt;code&gt;cy.wait(Number)&lt;/code&gt; is an anti-pattern and you can almost always replace it with an assertion. Read Cypress's &lt;a href="https://docs.cypress.io/guides/references/best-practices#Unnecessary-Waiting" rel="noopener noreferrer"&gt;detailed explanation and examples&lt;/a&gt; to understand more. If you succumb to the temptation to add cy.wait() anyway, they will eventually become a time-sink.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tweak Cypress's configuration to remove unnecessary disk I/O
&lt;/h3&gt;

&lt;p&gt;Cypress aims to "just work" and does this admirably. A configuration file is automatically created by Cypress on the first run. Some of the options here increase the disk I/O and hence slow down Cypress itself. The main culprits are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.cypress.io/guides/references/configuration#Videos" rel="noopener noreferrer"&gt;video&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.cypress.io/guides/references/configuration#Videos" rel="noopener noreferrer"&gt;videoUploadOnPasses&lt;/a&gt; &lt;em&gt;NOTE:&lt;/em&gt; This one only applies if you are also using Cypress's dashboard. By default Cypress uploads all videos when connected to Dashboard which you probably don't need.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.cypress.io/guides/references/configuration#Screenshots" rel="noopener noreferrer"&gt;screenshotOnRunFailure&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Turn these off or turn on only for nightly runs etc. where you are not worried about the time taken.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tag tests and run only the ones you need to
&lt;/h3&gt;

&lt;p&gt;Cypress's list of &lt;a href="https://docs.cypress.io/plugins/directory" rel="noopener noreferrer"&gt;curated plugins&lt;/a&gt; includes &lt;a href="https://github.com/cypress-io/cypress-grep" rel="noopener noreferrer"&gt;cyress-grep&lt;/a&gt;. Using this plugin, you can run tests by grepping their titles or custom tags you've added to each test. Use this ability to run only the necessary &lt;a href="https://en.wikipedia.org/wiki/Smoke_testing_(software)" rel="noopener noreferrer"&gt;smoke&lt;/a&gt; or feature-specific tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Incorporate Cypress into your CI/CD
&lt;/h3&gt;

&lt;p&gt;This one is a little harder to implement but worth the effort. Take the time to read and understand how Cypress can be plugged into your &lt;a href="https://docs.cypress.io/guides/continuous-integration/introduction" rel="noopener noreferrer"&gt;CI/CD pipeline&lt;/a&gt;. As with everything else, Cypress's documentation here is extensive. As your test suite grows, offloading running the suite to some other system may be more performant than running all the tests locally on your laptop.&lt;/p&gt;

</description>
      <category>cypress</category>
    </item>
    <item>
      <title>Extending a Component Library using Vuex</title>
      <dc:creator>Vivek Kodira</dc:creator>
      <pubDate>Mon, 07 Dec 2020 16:27:15 +0000</pubDate>
      <link>https://dev.to/feldspartech/extending-a-component-library-using-vuex-5aka</link>
      <guid>https://dev.to/feldspartech/extending-a-component-library-using-vuex-5aka</guid>
      <description>&lt;p&gt;At &lt;a href="https://www.feldspartech.com/" rel="noopener noreferrer"&gt;FeldsparTech&lt;/a&gt; we are working on a no-code platform called &lt;a href="https://www.feldspartech.com/atman" rel="noopener noreferrer"&gt;Atman&lt;/a&gt;. Atman's UI is a component library that works as a standalone application or can be embedded into another application to enable quick no-code workflows.&lt;/p&gt;

&lt;p&gt;One of our goals is to allow granular control over our components. If this only meant rendering - we would build &lt;a href="https://adamwathan.me/renderless-components-in-vuejs/" rel="noopener noreferrer"&gt;Renderless components&lt;/a&gt;. However, we also want the consuming application to be able to override, modify and augment behavior.&lt;/p&gt;

&lt;p&gt;The approach we have chosen to achieve this goal is to use a &lt;a href="https://vuex.vuejs.org/#what-is-a-state-management-pattern" rel="noopener noreferrer"&gt;Vuex&lt;/a&gt; store.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prerequisite: Both the consuming application and the library should import and use Vuex.&lt;/li&gt;
&lt;li&gt;We first need to dynamically register our library as a module in the application's store. This is possible through the &lt;a href="https://vuex.vuejs.org/guide/modules.html#dynamic-module-registration" rel="noopener noreferrer"&gt;registerModule&lt;/a&gt; method
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ourlibrary&lt;/span&gt;&lt;span class="dl"&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;$store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ourlibrary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Methods that need to be customizable are implemented as actions in a vuex module. Within the action, the behavior could vary:

&lt;ul&gt;
&lt;li&gt;If we want the consuming application to be able to &lt;strong&gt;&lt;em&gt;override&lt;/em&gt;&lt;/strong&gt; behavior, the &lt;em&gt;action&lt;/em&gt; first checks if a global &lt;em&gt;action&lt;/em&gt; is available and invokes it. If not, the &lt;em&gt;action&lt;/em&gt; proceeds to perform the default logic specified in the library.&lt;/li&gt;
&lt;li&gt;If we want the consuming application to &lt;strong&gt;&lt;em&gt;augment&lt;/em&gt;&lt;/strong&gt; behavior, the module &lt;em&gt;action&lt;/em&gt; first performs the internal logic and then invokes the global &lt;em&gt;action&lt;/em&gt; (or vice versa) to perform any augmentation as necessary.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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%2Fi%2F15ex2ilcmoobubyeemj5.jpg" 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%2Fi%2F15ex2ilcmoobubyeemj5.jpg" alt="https://dev-to-uploads.s3.amazonaws.com/i/15ex2ilcmoobubyeemj5.jpg" width="771" height="342"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Overriding behavior example
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="nf"&gt;overriddenMethod&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="nx"&gt;params&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;_actions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;overriddenMethod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;overriddenMethod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="p"&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//Default behavior&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;h3&gt;
  
  
  Augmenting behavior example
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="nf"&gt;augmentedMethod&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
     &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="cm"&gt;/*
        Internal logic which updates `response`
        ...
     */&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;_actions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;augmentedMethod&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;globalResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;augmentedMethod&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="p"&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="cm"&gt;/*
        Update `response` with `globalResponse`
    */&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&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 pattern is quite powerful. For instance, you can now even allow the consuming application to entirely replace a particular nested component with one of its own.&lt;/p&gt;

&lt;p&gt;Here, the vuex action functions as a &lt;a href="https://en.wikipedia.org/wiki/Factory_method_pattern" rel="noopener noreferrer"&gt;Factory&lt;/a&gt;. Now you can customize the logic as per your needs. The Overriding approach will entirely replace default component. The Augmentation approach will allow the consuming application to add business-specific components you may not want in the library.&lt;/p&gt;
&lt;h2&gt;
  
  
  Dynamic Component in the Library
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;template&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;component&lt;/span&gt; &lt;span class="na"&gt;v-if&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currentComponent"&lt;/span&gt; &lt;span class="na"&gt;v-bind&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="na"&gt;is&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currentComponent"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;component&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;template&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
export &lt;span class="si"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;mounted&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;currentComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&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;$store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;module&amp;gt;/deriveComponent`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Action in Library's store
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;deriveComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;currentComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;//augment or override as per your requirements&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;currentComponent&lt;/span&gt;

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

&lt;/div&gt;

&lt;h2&gt;
  
  
  Resources I found useful during this exercise
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Github template to create a vuetify component library
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/mitevpi" rel="noopener noreferrer"&gt;
        mitevpi
      &lt;/a&gt; / &lt;a href="https://github.com/mitevpi/vuetify-component-lib-template" rel="noopener noreferrer"&gt;
        vuetify-component-lib-template
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Template for creating a component library/design system using Vue.js and Vuetify.js.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
    &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/04056528fb46dbdcfac38d14d8465f269b055a8f926123d30e2ea10eb450d1cb/68747470733a2f2f63646e2e61757468302e636f6d2f626c6f672f7675656a732f7675652d6c6f676f2e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/04056528fb46dbdcfac38d14d8465f269b055a8f926123d30e2ea10eb450d1cb/68747470733a2f2f63646e2e61757468302e636f6d2f626c6f672f7675656a732f7675652d6c6f676f2e706e67" alt="Vue.js" width="100"&gt;&lt;/a&gt;
    &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/0fed18d041694558c0b11c60a0f52e668557a2b6c85bf00700ce1524c5e730b8/68747470733a2f2f7777772e736161736875622e636f6d2f696d616765732f6170702f736572766963655f6c6f676f732f382f3230656639306430346233352f6c617267652e706e673f31353237373431363039"&gt;&lt;img src="https://camo.githubusercontent.com/0fed18d041694558c0b11c60a0f52e668557a2b6c85bf00700ce1524c5e730b8/68747470733a2f2f7777772e736161736875622e636f6d2f696d616765732f6170702f736572766963655f6c6f676f732f382f3230656639306430346233352f6c617267652e706e673f31353237373431363039" alt="Vuetify.js" width="100"&gt;&lt;/a&gt;
    &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/17a58938ab5bd7de0a895991a3c309842f41b3e6a6fc4f0028489ca665bc4c61/68747470733a2f2f6f70656e636f6c6c6563746976652d70726f64756374696f6e2e73332d75732d776573742d312e616d617a6f6e6177732e636f6d2f30313166633632302d346362322d313165392d613531612d6664626231306234636162622e706e67"&gt;&lt;img src="https://camo.githubusercontent.com/17a58938ab5bd7de0a895991a3c309842f41b3e6a6fc4f0028489ca665bc4c61/68747470733a2f2f6f70656e636f6c6c6563746976652d70726f64756374696f6e2e73332d75732d776573742d312e616d617a6f6e6177732e636f6d2f30313166633632302d346362322d313165392d613531612d6664626231306234636162622e706e67" alt="Storybook" width="100"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Vue.js + Vuetify.js Component Library Template&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/a70df05a17ebb58d9ea33c41e4f52959757785d18eefb519f64170e2ca77378d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6973737565732f6d6974657670692f767565746966792d636f6d706f6e656e742d6c69622d74656d706c617465"&gt;&lt;img src="https://camo.githubusercontent.com/a70df05a17ebb58d9ea33c41e4f52959757785d18eefb519f64170e2ca77378d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6973737565732f6d6974657670692f767565746966792d636f6d706f6e656e742d6c69622d74656d706c617465" alt="GitHub issues"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/9a42446b1648ddf9fee389c5e89db998ec25737ea9aa3bdf1f0cb4fb5a680472/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6973737565732d70722d7261772f6d6974657670692f767565746966792d636f6d706f6e656e742d6c69622d74656d706c617465"&gt;&lt;img src="https://camo.githubusercontent.com/9a42446b1648ddf9fee389c5e89db998ec25737ea9aa3bdf1f0cb4fb5a680472/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6973737565732d70722d7261772f6d6974657670692f767565746966792d636f6d706f6e656e742d6c69622d74656d706c617465" alt="GitHub pull requests"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/ea5ed122ef6ad36610ef245a3f62d47dbbe94e9b9979966116d10bc87003d5ed/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f636f6e7472696275746f72732f6d6974657670692f767565746966792d636f6d706f6e656e742d6c69622d74656d706c617465"&gt;&lt;img src="https://camo.githubusercontent.com/ea5ed122ef6ad36610ef245a3f62d47dbbe94e9b9979966116d10bc87003d5ed/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f636f6e7472696275746f72732f6d6974657670692f767565746966792d636f6d706f6e656e742d6c69622d74656d706c617465" alt="GitHub contributors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/0e98792cc679939c86fc268ddcb2d4d9b51385717cafe58b752e6b49edf14fe8/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6173742d636f6d6d69742f6d6974657670692f767565746966792d636f6d706f6e656e742d6c69622d74656d706c617465"&gt;&lt;img src="https://camo.githubusercontent.com/0e98792cc679939c86fc268ddcb2d4d9b51385717cafe58b752e6b49edf14fe8/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6173742d636f6d6d69742f6d6974657670692f767565746966792d636f6d706f6e656e742d6c69622d74656d706c617465" alt="GitHub last commit"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/4c779de60571248858113f714384536932f75ff367ea526393c164ebfa971a9d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f72656c656173652d646174652f6d6974657670692f767565746966792d636f6d706f6e656e742d6c69622d74656d706c617465"&gt;&lt;img src="https://camo.githubusercontent.com/4c779de60571248858113f714384536932f75ff367ea526393c164ebfa971a9d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f72656c656173652d646174652f6d6974657670692f767565746966792d636f6d706f6e656e742d6c69622d74656d706c617465" alt="GitHub Release Date"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/076ced63fabefda00f4b3257e68ba85049712035b467af43aa33db3cc9e12258/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c616e6775616765732f636f64652d73697a652f6d6974657670692f767565746966792d636f6d706f6e656e742d6c69622d74656d706c617465"&gt;&lt;img src="https://camo.githubusercontent.com/076ced63fabefda00f4b3257e68ba85049712035b467af43aa33db3cc9e12258/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c616e6775616765732f636f64652d73697a652f6d6974657670692f767565746966792d636f6d706f6e656e742d6c69622d74656d706c617465" alt="GitHub code size in bytes"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/12db9acc10b79522424f3f34100b74c476052ece214e727dc101ca5dc14dc6ec/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f7265706f2d73697a652f6d6974657670692f767565746966792d636f6d706f6e656e742d6c69622d74656d706c617465"&gt;&lt;img src="https://camo.githubusercontent.com/12db9acc10b79522424f3f34100b74c476052ece214e727dc101ca5dc14dc6ec/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f7265706f2d73697a652f6d6974657670692f767565746966792d636f6d706f6e656e742d6c69622d74656d706c617465" alt="GitHub repo size"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/f6771bd09ec339185bd869fb81cbf422443932a2c5751b937d6eb7b8d87b3d2d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6d6974657670692f767565746966792d636f6d706f6e656e742d6c69622d74656d706c617465"&gt;&lt;img src="https://camo.githubusercontent.com/f6771bd09ec339185bd869fb81cbf422443932a2c5751b937d6eb7b8d87b3d2d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6d6974657670692f767565746966792d636f6d706f6e656e742d6c69622d74656d706c617465" alt="GitHub"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A Design System / Component Library Template for enabling graphic
consistency and legibility across web development projects by creating
reusable components and styles with accessible &amp;amp; legible documentation
Built on top of Vue.js and &lt;a href="https://vuetifyjs.com/en/" rel="nofollow noopener noreferrer"&gt;Vuetify.js&lt;/a&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;This Template is comprised of two major categories: &lt;a href="https://github.com/mitevpi/vuetify-component-lib-templatesrc/styles" rel="noopener noreferrer"&gt;Styles&lt;/a&gt;
and &lt;a href="https://github.com/mitevpi/vuetify-component-lib-templatesrc/components" rel="noopener noreferrer"&gt;Components&lt;/a&gt;. It builds to compiled components
(&lt;code&gt;.js&lt;/code&gt;) and compiled styles (&lt;code&gt;.css&lt;/code&gt;) from the source (&lt;code&gt;.vue&lt;/code&gt;) files
which can be used across web applications. It also creates a
&lt;a href="https://storybook.js.org/" rel="nofollow noopener noreferrer"&gt;Storybook&lt;/a&gt; site for component/design system
documentation.&lt;/p&gt;

&lt;p&gt;Clone this repository locally, and use it as a starting point for
building a component library / design system on top of Vue.js and
&lt;a href="https://vuetifyjs.com/en/" rel="nofollow noopener noreferrer"&gt;Vuetify.js&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Added/Updated Configurations to Vue CLI Starter&lt;/h2&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://github.com/airbnb/javascript" rel="noopener noreferrer"&gt;AirBnB Style Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://prettier.io/" rel="nofollow noopener noreferrer"&gt;Prettier Style Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://eslint.org/" rel="nofollow noopener noreferrer"&gt;ESLint (Style Enforcing)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://storybook.js.org/" rel="nofollow noopener noreferrer"&gt;Storybook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cli.vuejs.org/guide/build-targets.html#library" rel="nofollow noopener noreferrer"&gt;Library Build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sass/node-sass" rel="noopener noreferrer"&gt;SCSS/SASS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vuetifyjs.com/en/" rel="nofollow noopener noreferrer"&gt;Vuetify.js&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Key Commands&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;After cloning/downloading the repository locally, install the required
dependencies using…&lt;/p&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/mitevpi/vuetify-component-lib-template" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;

&lt;/li&gt;

&lt;li&gt;A step-by-step series by siegerts on how to create a Vue JS component library

&lt;div class="ltag__link"&gt;
  &lt;a href="/siegerts" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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%2Fuser%2Fprofile_image%2F424003%2Fee361bd1-6389-4720-9e06-dfa72626ceb1.jpeg" alt="siegerts"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/siegerts/creating-a-vue-js-component-library-part-i-introduction-2o9f" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Creating a Vue.js component library: Part I - Introduction&lt;/h2&gt;
      &lt;h3&gt;siegerts ・ Jul 7 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#vue&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#library&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#plugin&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#vuepress&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Also cross-posted on &lt;a href="https://feldspar-tech.medium.com/extending-a-component-library-using-vuex-965b45f0bfcc" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vue</category>
      <category>vuex</category>
    </item>
    <item>
      <title>Online Reading — Why it fails for some formats</title>
      <dc:creator>Vivek Kodira</dc:creator>
      <pubDate>Sat, 21 Nov 2020 14:05:04 +0000</pubDate>
      <link>https://dev.to/feldspartech/online-reading-why-it-fails-for-some-formats-3pp6</link>
      <guid>https://dev.to/feldspartech/online-reading-why-it-fails-for-some-formats-3pp6</guid>
      <description>&lt;p&gt;Did you know that the world does not know whom to credit as the inventor of E-books? There are contenders for the title from as far back as 1946. &lt;a href="https://en.wikipedia.org/wiki/E-book#Inventor" rel="noopener noreferrer"&gt;Ref&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For you and me, our first time experiencing e-books were as PDF documents. These were typically technical journals and pirated scanned versions of popular books.&lt;/p&gt;

&lt;p&gt;It took some time for traditional publishers to accept this new format and begin selling them but since then e-books have exploded. Project Gutenberg now has over 60000 books one can read for free. For many of us, E-books are now the preferred format. This could be for several reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cost (an e-book is typically cheaper than the physical alternative)&lt;/li&gt;
&lt;li&gt;Space (e-books don’t take up any space. You can carry a thousand on a holiday)&lt;/li&gt;
&lt;li&gt;Convenience (you can read an e-book in the dark)&lt;/li&gt;
&lt;li&gt;Environmental impact (e-books don’t need paper and so fewer )&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But even the most ardent fans of the format will admit that there are disadvantages.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;With e-books we lose the tactile experience of touching and feeling a physical book&lt;/li&gt;
&lt;li&gt;Some formats, graphic novels, comics and childrens’ books for instance do not lend themselves to an e-book format
The second disadvantage is the focus of this blog.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Graphic Novels and Comics as Ebooks
&lt;/h2&gt;

&lt;p&gt;With the limited screen real estate in small devices, text such as speech bubbles in graphic novels and comics are often not seen clearly. Google and Amazon, the big two players in the e-book market both address this in similar ways — by zooming in certain portions of the page.&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%2Fi%2Fxdi2owo7qhjf37wnwirf.gif" 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%2Fi%2Fxdi2owo7qhjf37wnwirf.gif" alt="Amazon’s Guided view" width="571" height="1015"&gt;&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%2Fi%2Fuxxt6n9ocjr0g3mzo0jk.gif" 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%2Fi%2Fuxxt6n9ocjr0g3mzo0jk.gif" alt="Alt Text" width="350" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Google’s Bubble Zoom enhances speech bubbles in each panel. Amazon’s Guided View  navigates a comic panel-by-panel&lt;/p&gt;

&lt;p&gt;These approaches address one problem but end up creating another. Arbitrarily zooming in on certain portions and truncating others may make the text legible but harm the overall experience.&lt;/p&gt;

&lt;p&gt;The focus on the textual content mean that user loses out on experiencing the art.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you do justice to art?
&lt;/h2&gt;

&lt;p&gt;Graphic Novels, comics and Childrens’ books are as much about the illustrations and the art as the story.&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%2Fi%2F3j2hbhzcs40fizro8kl3.jpeg" 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%2Fi%2F3j2hbhzcs40fizro8kl3.jpeg" alt="Watchmen" width="700" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The art in such books are a labour of love and painstakingly detailed. Mobile Form factors do not render these as originally intended by the artist.&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%2Fi%2Fstgl3r9rw3g8c89hdh2j.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%2Fi%2Fstgl3r9rw3g8c89hdh2j.png" alt="Gosu" width="209" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Artists and authors recognize the issue and are beginning to address this problem in new ways. Webtoons, a site with mostly South Korean artists, formats all content in vertical panels with very large text bubbles — this is art designed specifically for consumption on mobile devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Graphics are not just for entertainment
&lt;/h2&gt;

&lt;p&gt;Before we dismiss this problem as trivial and relevant only to entertainment, consider that graphics are an integral portion of most technical manuals. To quote a cliche “a picture is worth a thousand words”.&lt;/p&gt;

&lt;p&gt;Today, graphical novels are being used for more than just comics. Google has used them to explain the inner workings of a browser. Universities use them to explain copyright laws. So this isn’t a challenge to be taken lightly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where do we go from here?
&lt;/h2&gt;

&lt;p&gt;Content creators are evolving to produce art that is more easily rendered on mobile devices. E-book stores are creating technology to help improve a consumer’s experience of consuming such content. However, convenience and market forces are not the only driving factors. As technology makes them more affordable, people who can afford it, are moving to other larger form factors such as tablets. These solve the problem of real estate.&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://www.feldspartech.com/" rel="noopener noreferrer"&gt;FeldsparTech&lt;/a&gt;, one of our design tenets is to model technology after nature and the real world. We believe that reading a digital book should be as similar to reading a physical one as we can make it. And that everything else that consuming digital media typically entails (algorithms, annoying ads) should be removed or reduced from the user’s experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/E-book#Inventor" rel="noopener noreferrer"&gt;Ebook Inventor — Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.theverge.com/2016/7/21/12229074/google-play-books-bubble-zoom-machine-learning-sdcc-2016" rel="noopener noreferrer"&gt;Google’s new Bubble Zoom feature will make reading comics on your phone easier&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.comixology.com/new-to-comixology" rel="noopener noreferrer"&gt;Comics by comiXology&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.google.com/googlebooks/chrome/" rel="noopener noreferrer"&gt;Google Chrome&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.law.duke.edu/cspd/comics/zoomcomic.html" rel="noopener noreferrer"&gt;CSPD Zoomed Comic&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also posted &lt;a href="https://feldspar-tech.medium.com/online-reading-why-it-fails-for-some-formats-53714754e568" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>books</category>
      <category>startup</category>
    </item>
    <item>
      <title>Sunk Cost Fallacy and Books</title>
      <dc:creator>Vivek Kodira</dc:creator>
      <pubDate>Sat, 21 Nov 2020 13:50:51 +0000</pubDate>
      <link>https://dev.to/feldspartech/sunk-cost-fallacy-and-books-15l8</link>
      <guid>https://dev.to/feldspartech/sunk-cost-fallacy-and-books-15l8</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“Individuals commit the sunk cost fallacy when they continue a behavior or endeavor as a result of previously invested resources (time, money or effort)&lt;br&gt;
(Arkes &amp;amp; Blumer, 1985)”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;How often have you read a terrible book all the way through just because you have already paid for it or read halfway through it. This is the sunk cost fallacy at play.&lt;/p&gt;

&lt;p&gt;Online book stores allow you to preview books — read the first few pages before deciding. But what if you decide you don’t like a book halfway through?&lt;/p&gt;

&lt;p&gt;There are two factors that typically contribute to the sunk cost fallacy — one is the effort and time already invested, the second is the actual money already spent.&lt;/p&gt;

&lt;p&gt;There is not much one can do about the former, but the financial cost is definitely something that can be addressed. There are two ways to do this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The easiest is to reduce the cost so that you the consumer don’t have to worry about it when deciding what to do next. This approach however is zero-sum — i.e to benefit you, the creator of the content you are consuming will have to suffer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Another is a pay-per-use model. You pay only for what you consume. When you decide to stop, you can. But not all media lends itself to this model; books for instance. But what if there was a way to implement pay-per-use for books?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We at &lt;a href="https://www.feldspartech.com/" rel="noopener noreferrer"&gt;FeldsparTech&lt;/a&gt; think we know of a way and can’t wait to show you what we have in mind.&lt;/p&gt;

&lt;p&gt;Also posted &lt;a href="https://feldspar-tech.medium.com/sunk-cost-fallacy-and-books-3cb5bc9c12d0" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>books</category>
      <category>startup</category>
    </item>
    <item>
      <title>How we Converted our Products into a Platform</title>
      <dc:creator>Vivek Kodira</dc:creator>
      <pubDate>Mon, 09 Mar 2020 18:02:03 +0000</pubDate>
      <link>https://dev.to/jfrog/how-we-converted-our-products-into-a-platform-32of</link>
      <guid>https://dev.to/jfrog/how-we-converted-our-products-into-a-platform-32of</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AQctmGSy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sr18jm0utakd3uay1jtu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AQctmGSy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sr18jm0utakd3uay1jtu.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Organisations which design systems […] are constrained to produce designs which are copies of the communication structures of these organisations. &lt;br&gt;
    — M. Conway&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;JFrog’s products cover a broad range of DevOps processes: from Artifactory which helps development teams manage binaries, to Mission Control which helps manage Artifactory instances, Xray which helps avoid security vulnerabilities and Distribution which helps distribute binaries in a large organisation.&lt;/p&gt;

&lt;p&gt;Over time, Conway’s law has applied to our products. Despite our best efforts, our products had begun to diverge in their user experience, architecture, interfaces and design choices. &lt;/p&gt;

&lt;p&gt;A chief goal of the JFrog Platform was to fix this divergence and provide a unified experience to our users. A platform comes with several benefits - this blog would be twice as long if I listed them all. Instead, you can read more about them &lt;a href="https://jfrog.com/blog/jfrog-devops-platform/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Instead, I will  focus on answering the question: &lt;em&gt;How do you go about converting several products into a single platform?&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;This was a mammoth exercise involving every team in JFrog. This blog will explain how we in JFrog RnD broke down the task. &lt;/p&gt;

&lt;p&gt;When we considered what areas end-users interact with most in our products, the following stood out.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User Experience&lt;/li&gt;
&lt;li&gt;Installation&lt;/li&gt;
&lt;li&gt;Configuration&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  User Experience
&lt;/h2&gt;

&lt;p&gt;We’ve always had a common base library of components and styles which every UI team consumed, but now we went all the way and created a single pane of glass for all our products: A Unified UI.&lt;/p&gt;

&lt;p&gt;To create a unified UI, we restructured our codebases. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Every product’s UI code was extracted and merged into a single one. &lt;em&gt;A thin backend layer was introduced to ensure requests were still routed to the original product. Developers still remained part of product teams: ensuring deeper domain knowledge and expertise.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pull Requests would now be available to be reviewed by the entire Front End Developers (FEDs) team. &lt;em&gt;This process initially introduced some delays but made up for it by improving our quality and standardisation.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Common modules (such as global search, permissions, users, groups, roles, etc.) were dropped from individual products and rewritten to be generic across products.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installers
&lt;/h2&gt;

&lt;p&gt;Our products can be installed on several different Operating Systems. Some, like Artifactory, appear monolithic and others create several microservices. Artifactory, can be configured to work with PostgreSQL, Oracle, MySQL, MS SQL or MariaDB while others need explicit databases to function (ex: Mission Control needs Elasticsearch, Xray: RabbitMQ, Distribution: Redis).&lt;/p&gt;

&lt;p&gt;Moreover, not all users would want every product pre-bundled in the platform. They would want to be able to choose exactly what they install. This necessitated an installation process that allowed all the products to work together seamlessly while still being discrete entities.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A dedicated team was created to develop and maintain installers.&lt;br&gt;
&lt;em&gt;This team collected feedback from Support engineers, Architects and product RnD groups. Its goals were to standardise the user experience of our installers while maintaining backward compatibility.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A single repository was created for shared code and routines.&lt;br&gt;
&lt;em&gt;This allowed the team to abstract common logic and put them in a single location&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This repository is referenced in each product’s build to create the final installer. &lt;em&gt;This allowed product-level customisation of the installation process where necessary.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An important outcome of this restructuring is that all our installers feel similar. For instance, every product installation variant now also contains an interactive wrapper script which asks similar questions. Another is that all our products are now installed with a standard folder structure. You can read more about this structure in &lt;a href="https://dev.to/jfrog/the-jfrog-platform-the-new-file-system-layout-f1n"&gt;Eldad’s blog&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;Our products are written in several different programming languages. Java is the most common, but Go is fast becoming popular. This has meant that how we read configuration is also very different between products. Some read property files, others: XML, etc.&lt;/p&gt;

&lt;p&gt;For the unified platform, we decided to standardise this to a single configuration file: the &lt;a href="https://www.jfrog.com/confluence/display/JFROG/System+YAML+Configuration+File"&gt;system.yaml&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;To implement this, we needed process and code changes. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We developed a common configuration manager library in every language we needed it in: bash, go, node, java. &lt;em&gt;We did briefly consider creating a shared “configuration microservice” but dismissed it - since reading configuration is such a basic task which shouldn’t need API calls.&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We then mandated that every microservice should read configuration using only this library. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Logging
&lt;/h2&gt;

&lt;p&gt;Unifying Logging was critical. Doing so ensured a consistent troubleshooting experience, easier support, better automation and easier integration with third-party log aggregators.&lt;/p&gt;

&lt;p&gt;JFrog’s software architects came up with a proposed structure. Each team only had to change the configuration of the logger library. Or in case of languages like bash, which do not support Log4j, implement the new structure. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;One of the outcomes of this exercise is that all &lt;a href="https://www.jfrog.com/confluence/display/JFROG/Logging"&gt;our logs&lt;/a&gt; now follow a standard structure. The platform also produces a new &lt;a href="https://www.jfrog.com/confluence/display/JFROG/Logging#Logging-ConsoleLog.1"&gt;console.log&lt;/a&gt; which aggregates logs from all our products’ microservices and shell scripts into a single file.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  APIs
&lt;/h2&gt;

&lt;p&gt;We needed to implement a consistent, and futuristic approach for APIs while also ensuring backward compatibility. We did this by retaining our individual product APIs, but adding a layer around them which would allow incremental standardisation.&lt;/p&gt;

&lt;p&gt;If you inspect the microservices which each product now starts up, you’ll notice that one is repeated in every product: a router.&lt;/p&gt;

&lt;p&gt;The router forms the backbone of all api-based communication between microservices within a product, between products, and between a product and the outside world. It takes care of network-level details, allowing product developers to focus solely on business logic.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To implement this, we formed a dedicated team whose mandate was to manage, standardise, and optimise communication. &lt;/li&gt;
&lt;li&gt;Artefacts produced by this team are bundled in every product’s build and used as microservices.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Converting our products to the unified platform required a combination of organisational restructuring, architectural, design &amp;amp; process changes. This last year, our teams have evolved along with the product itself. The experience has been an enriching one, and one we hope will show in the product we are proud to have created.&lt;/p&gt;

</description>
      <category>jfrog</category>
      <category>jfrogplatform</category>
      <category>artifactory</category>
      <category>xray</category>
    </item>
    <item>
      <title>Lessons Learned Building JFrog Platform Installers: Applying the Template pattern to Shell Scripts</title>
      <dc:creator>Vivek Kodira</dc:creator>
      <pubDate>Tue, 25 Feb 2020 16:27:45 +0000</pubDate>
      <link>https://dev.to/jfrog/lessons-learned-building-jfrog-platform-installers-applying-the-template-pattern-to-shell-scripts-4ac7</link>
      <guid>https://dev.to/jfrog/lessons-learned-building-jfrog-platform-installers-applying-the-template-pattern-to-shell-scripts-4ac7</guid>
      <description>&lt;p&gt;&lt;a href="https://www.jfrog.com/confluence/display/JFROG/JFrog+Artifactory" rel="noopener noreferrer"&gt;Artifactory&lt;/a&gt;, JFrog’s flagship product, has been in the market for more than a decade now. Individual developers working on Open source projects, Startups - big and small, and large Enterprises all use it to store binaries. Over time, Artifactory has shipped with every installation type imaginable: from ZIP files, to native installers &amp;amp; Helm charts.&lt;/p&gt;

&lt;p&gt;Over the years - to further our vision of &lt;a href="https://jfrog.com/whitepaper/a-vision-of-liquid-software/" rel="noopener noreferrer"&gt;Liquid software&lt;/a&gt;, we’ve launched other products: &lt;a href="https://www.jfrog.com/confluence/display/JFROG/JFrog+Xray" rel="noopener noreferrer"&gt;Xray&lt;/a&gt;, &lt;a href="https://www.jfrog.com/confluence/display/JFROG/JFrog+Mission+Control" rel="noopener noreferrer"&gt;Mission Control&lt;/a&gt; &amp;amp; &lt;a href="https://www.jfrog.com/confluence/display/JFROG/JFrog+Distribution" rel="noopener noreferrer"&gt;Distribution&lt;/a&gt; in this ecosystem.&lt;/p&gt;

&lt;p&gt;As each product matured, their installers began to rely on interactive installation scripts.&lt;/p&gt;

&lt;p&gt;Interactive installation scripts offer an easier experience to an end user. To product teams, they are attractive because they: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Offer finer grained control over the installation experience&lt;/li&gt;
&lt;li&gt;Reduce need for elaborate documentation covering all possibilities&lt;/li&gt;
&lt;li&gt;Make user errors less likely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Last year, at &lt;a href="https://www.youtube.com/watch?v=7XS5m6cgPYk" rel="noopener noreferrer"&gt;SwampUp 2019&lt;/a&gt;, we announced the new JFrog Unified Platform. Our aim was to provide a unified experience to all users of our products. One of these experiences was the installation of our products.&lt;/p&gt;

&lt;p&gt;From an installation perspective, our goals for the installation experience were that each be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backward compatible&lt;/li&gt;
&lt;li&gt;Easy to use&lt;/li&gt;
&lt;li&gt;Unified (Similar, work well with each other)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these in mind, all our products now ship with interactive installation scripts. &lt;em&gt;The one notable exception is the native installation package for Artifactory - which, we felt was simple to install.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The experience of building installers for JFrog products was challenging and enriching for my team and me. We learned much during the process about customer focus, enhancing support-ability, design and user experience.&lt;/p&gt;

&lt;p&gt;The rest of this blog deals with one specific set of lessons: those learned from applying the Template pattern to shell scripts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Template Pattern
&lt;/h2&gt;

&lt;p&gt;At a very high level, what an interactive installation script does is very simple and is demonstrated in the diagram below.&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%2Fi%2Fna8u3tbz920pihn9grur.jpg" 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%2Fi%2Fna8u3tbz920pihn9grur.jpg" alt="high-level overview of an interactive installation" width="695" height="115"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Things get complicated when we get into details. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each product has its own microservices. (Read more about our &lt;a href="https://www.jfrog.com/confluence/display/JFROG/System+Architecture" rel="noopener noreferrer"&gt;System Architecture here&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Each uses different databases. For instance, Xray uses RabbitMQ for messaging, Distribution uses Redis and Mission Control: ElasticSearch. (Read more about the Databases we use and support in &lt;a href="https://www.jfrog.com/confluence/display/JFROG/Common+Resources" rel="noopener noreferrer"&gt;Common Resources&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem statement then is &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“We have a common sequence of operations but each step in the operation is implemented differently“&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This, we realised, was simply the &lt;a href="https://en.wikipedia.org/wiki/Template_method_pattern" rel="noopener noreferrer"&gt;Template pattern&lt;/a&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Template method is one of the behavioral design patterns identified by Gamma et al  in the book Design Patterns. &lt;/p&gt;

&lt;p&gt;The template method is a method in a superclass, usually an abstract superclass, and defines the skeleton of an operation in terms of a number of high-level steps. These steps are themselves implemented by additional helper methods in the same class as the template method.&lt;/p&gt;

&lt;p&gt;The intent of the template method is to define the overall structure of the operation, while allowing subclasses to refine, or redefine, certain steps&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most programming languages, such as Java, have support for class hierarchies and abstract methods which make implementing the template pattern straightforward. Bash, the language we wrote our interactive scripts in, does not have the same language constructs. Therefore we had to improvise. However, it turned out to be fairly simple.&lt;/p&gt;

&lt;h2&gt;
  
  
  Template Pattern - Approach 1
&lt;/h2&gt;

&lt;p&gt;One approach was to simply encapsulate the sequence within a generic script with a main method. The user would invoke the generic script. The script in turn, would invoke the product-specific hooks.&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%2Fi%2Fphwvjybrptf08op6mjbg.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%2Fi%2Fphwvjybrptf08op6mjbg.png" alt="Approach 1" width="800" height="865"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that in this approach&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each hook needs to be explicitly coded for. The product-specific script cannot decide what methods it wants to override.&lt;/li&gt;
&lt;li&gt;Each hook has to be implemented (even if with a dummy implementation) in the product-specific script. Not doing so will result in a runtime error.&lt;/li&gt;
&lt;li&gt;Variables available to the parent script, unless they are also exported as environment variables, are NOT available to the product-specific script and vice-versa.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach is particularly suited (and even recommended) for scenarios where you are invoking a third-party script - one whose internals you don’t care about and with which you don’t want to share too much of your internal state, etc. &lt;/p&gt;

&lt;h2&gt;
  
  
  Template Pattern - Approach 2
&lt;/h2&gt;

&lt;p&gt;Consider an alternative approach, depicted by the sequence diagram below:&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%2Fi%2Fsog5dkpyaihhwiaagp3a.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%2Fi%2Fsog5dkpyaihhwiaagp3a.png" alt="Approach 2" width="800" height="671"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Here, the user invokes the product-specific script. &lt;em&gt;This allows us to bootstrap with product-specific variables before beginning the common sequence of operations&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;The product-specific script &lt;code&gt;sources&lt;/code&gt; the generic script and invokes its main method. &lt;em&gt;This allows variables to be shared across both scripts.&lt;/em&gt; &lt;/li&gt;
&lt;li&gt;Sourcing also facilitates &lt;code&gt;method overriding&lt;/code&gt;. Methods in the product-specific script, which appear below the sourced script’s methods, override the ones in the generic script.&lt;/li&gt;
&lt;li&gt;This approach also avoids unnecessary code duplication. The generic script already contains dummy or actual method implementations. &lt;em&gt;Only hooks which need to be implemented in the product-specific script need to be added to it.&lt;/em&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach is more suitable when you &lt;em&gt;own&lt;/em&gt; both scripts and want them to share information. &lt;/p&gt;

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

&lt;p&gt;Implementing the template pattern allowed us to control the overall installation experience while being able to accommodate every product’s particular quirks. It made our design more agile and even allowed us to easily incorporate changes later in the development cycle, from our beta users. &lt;/p&gt;

&lt;p&gt;Do let me know in the comments if you found this blog’s contents useful and would like to know more about the other lessons we learned. &lt;/p&gt;

&lt;p&gt;References&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Template_method_pattern" rel="noopener noreferrer"&gt;Template Pattern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The Flow chart was created using &lt;a href="https://www.draw.io/" rel="noopener noreferrer"&gt;https://www.draw.io/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The Sequence diagrams were created using &lt;a href="https://www.websequencediagrams.com/" rel="noopener noreferrer"&gt;https://www.websequencediagrams.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;This article is also posted on &lt;a href="https://medium.com/@vivekkodira/lessons-learned-building-jfrog-platform-installers-applying-the-template-pattern-to-shell-scripts-f85234b70316" rel="noopener noreferrer"&gt;medium&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>jfrog</category>
      <category>shellscript</category>
      <category>designpattern</category>
    </item>
    <item>
      <title>Automate a multi-window experience on iTerm2</title>
      <dc:creator>Vivek Kodira</dc:creator>
      <pubDate>Sun, 18 Aug 2019 05:11:05 +0000</pubDate>
      <link>https://dev.to/vivekkodira/automate-a-multi-window-experience-on-iterm2-2j9e</link>
      <guid>https://dev.to/vivekkodira/automate-a-multi-window-experience-on-iterm2-2j9e</guid>
      <description>&lt;h2&gt;
  
  
  TLDR
&lt;/h2&gt;

&lt;p&gt;This post is a tutorial. The final outcome will be a script that creates several sessions on a remote machine, running commands in parallel. Jump to the end to see the final script or read on for a step-by-step tutorial&lt;/p&gt;

&lt;h2&gt;
  
  
  Why would you want iTerm automation?
&lt;/h2&gt;

&lt;p&gt;Here is an example from my own workflow:&lt;/p&gt;

&lt;p&gt;The installation script I'm testing these days launches several containers, creates folders with different user permissions and copies files back and forth between mounted folders.&lt;/p&gt;

&lt;p&gt;These are the steps I need to do each time I begin testing&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create 4 sessions in an iTerm tab&lt;/li&gt;
&lt;li&gt;ssh into the remote machine I'm testing*&lt;/li&gt;
&lt;li&gt;switch to sudo*&lt;/li&gt;
&lt;li&gt;Install the software I will need (&lt;code&gt;docker&lt;/code&gt;, &lt;code&gt;docker-compose&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;Configure &lt;code&gt;watch&lt;/code&gt; to monitor docker containers in one session&lt;/li&gt;
&lt;li&gt;Configure &lt;code&gt;tree&lt;/code&gt; to watch the folder structure in another&lt;/li&gt;
&lt;li&gt;Configure &lt;code&gt;watch&lt;/code&gt; with &lt;code&gt;cat&lt;/code&gt; to monitor some files in the third&lt;/li&gt;
&lt;li&gt;And run the installation script in the last container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;*Repeated in each session!&lt;/p&gt;

&lt;p&gt;You can see how doing this over and over becomes tedious. Shell scripts would be able to automate some steps above but not enough.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.apple.com/library/archive/documentation/AppleScript/Conceptual/AppleScriptLangGuide/introduction/ASLR_intro.html" rel="noopener noreferrer"&gt;AppleScript&lt;/a&gt; to the rescue!&lt;/p&gt;

&lt;h2&gt;
  
  
  Hello world!
&lt;/h2&gt;

&lt;p&gt;Let's start with a hello world.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a file &lt;code&gt;hello_world.scpt&lt;/code&gt; with the contents below:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/osascript
tell application "iTerm2"
    set contentToPrint to "hello world"
end tell
do shell script "echo " &amp;amp; contentToPrint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Run this script (&lt;code&gt;osascript hello_world.scpt&lt;/code&gt;). You should see
&lt;code&gt;hello world&lt;/code&gt; in the terminal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code above is reasonably self-explanatory, so let's jump right into the next example. &lt;/p&gt;

&lt;h2&gt;
  
  
  Hello world .. again?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create a file &lt;code&gt;hello_world2.scpt&lt;/code&gt; with the contents below:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/osascript
tell application "iTerm2"
    tell current session of current tab of current window
        write text "echo 'hello world again!'"
    end tell
end tell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Run this script (&lt;code&gt;osascript hello_world2.scpt&lt;/code&gt;). You should see
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~ osascript hello_world2.scpt
echo 'hello world again!'
~ echo 'hello world again!'
hello world again!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Though the final output is similar, this version printed a few more lines as well. How is this different from the earlier script? &lt;/p&gt;

&lt;p&gt;Press the 'up arrow' to see the last command in the terminal's history. It'll display &lt;/p&gt;

&lt;p&gt;&lt;code&gt;echo 'hello world again'&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Press up again and &lt;strong&gt;this&lt;/strong&gt; time you'll see &lt;/p&gt;

&lt;p&gt;&lt;code&gt;osascript hello_world2.scpt&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The difference then is this: &lt;code&gt;hello_world2.scpt&lt;/code&gt; executed the instruction we gave it on the terminal. The &lt;code&gt;echo&lt;/code&gt; got added to the terminal history.&lt;/p&gt;

&lt;p&gt;Personally, I prefer this second approach  - it is both more readable and I want to be able to search for and execute these individual commands if I need to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating multiple sessions
&lt;/h2&gt;

&lt;p&gt;Let's now automate creation of multiple sessions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a file &lt;code&gt;sessions.scpt&lt;/code&gt; with the content below
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/osascript
tell application "iTerm2"
    tell current session of current tab of current window
        split horizontally with default profile
    end tell
end tell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it and you'll see that it will divide the terminal into two horizontal sessions&lt;/p&gt;

&lt;p&gt;Similarly, running a script with the content below will divide the terminal into two vertical sessions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/osascript
tell application "iTerm2"
    tell current session of current tab of current window
        split vertically with default profile
    end tell
end tell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sessionception!
&lt;/h2&gt;

&lt;p&gt;You can issue commands to each session. Let's create two vertical sessions within each horizontal session. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a file &lt;code&gt;sessionsception.scpt&lt;/code&gt; with the content below
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/osascript
tell application "iTerm2"
    tell current session of current tab of current window
        split horizontally with default profile
        split vertically with default profile
    end tell
    tell third session of current tab of current window
        split vertically with default profile
    end tell
end tell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The part that might be confusing is figuring out which is the second, third and fourth sessions. Just remember that sessions are not numbered in the order they are created. Session numbering is left-to-right on the first row and then similarly on subsequent rows. &lt;/p&gt;

&lt;p&gt;This is why even though the horizontal session was created second, when addressing it in the script, the command is &lt;code&gt;tell third session&lt;/code&gt;&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%2F6f5je6oyjva0sysotdez.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%2F6f5je6oyjva0sysotdez.png" alt="session numbering" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Back to the future
&lt;/h2&gt;

&lt;p&gt;Let's jump ahead to the final script I need. Here it is below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/osascript
tell application "iTerm2"
    tell current session of current tab of current window
        write text "setupTargetMc"
        write text "sshmc2"
        write text "sudo su"
        write text "./installStuff.sh"
        write text "watch -n1 'docker ps --format=\"{{.Names}} {{.Ports}} {{.Status}}\"'"
        split horizontally with default profile
        split vertically with default profile
    end tell
    tell second session of current tab of current window
        write text "sshmc2"
        write text "sudo su"
        write text "watch -n1 'tree -u -d  ~/.temp -L 3'"
        split vertically with default profile
    end tell
    tell third session of current tab of current window
        write text "sshmc2"
        write text "sudo su"
        write text "watch -n1 'cat ~/.temp/key'"
    end tell
    tell fourth session of current tab of current window
        write text "sshmc2"
        write text "sudo su"
        write text "clear"
    end tell
end tell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much of the content above should be simple to understand by now. But here, if you need it, is a breakdown of some lines which may be unfamiliar &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;write text "setupTargetMc"&lt;/code&gt; - invokes &lt;code&gt;setupTargetMc&lt;/code&gt; - an alias I've created beforehand to copy scripts I need (&lt;code&gt;installStuff.sh&lt;/code&gt;) to the target machine&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;write text "sshmc2"&lt;/code&gt; - invokes &lt;code&gt;sshmc2&lt;/code&gt; - an alias I've created beforehand to &lt;code&gt;ssh&lt;/code&gt; into the target machine&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;write text "./installStuff.sh"&lt;/code&gt; - triggers &lt;code&gt;./installStuff.sh&lt;/code&gt;. This is the script we copied over in the first step. It will setup the machine to my requirements including installations of all the software I need&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;watch&lt;/code&gt; is used in several sessions, together with other commands, to continuously monitor docker containers, folder structures and contents of files&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;iTerm automation has saved me a lot of time and effort. Hopefully, this post will motivate you to try it and give you the means to write your own. Best of luck!&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.iterm2.com/documentation.html" rel="noopener noreferrer"&gt;iTerm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/library/archive/documentation/AppleScript/Conceptual/AppleScriptLangGuide/introduction/ASLR_intro.html" rel="noopener noreferrer"&gt;AppleScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linuxize.com/post/linux-watch-command/" rel="noopener noreferrer"&gt;watch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://formulae.brew.sh/formula/tree" rel="noopener noreferrer"&gt;tree&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>iterm2</category>
      <category>applescript</category>
      <category>macos</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Using Badges in iTerm2</title>
      <dc:creator>Vivek Kodira</dc:creator>
      <pubDate>Sat, 06 Jul 2019 15:50:29 +0000</pubDate>
      <link>https://dev.to/vivekkodira/using-badges-in-iterm2-4cce</link>
      <guid>https://dev.to/vivekkodira/using-badges-in-iterm2-4cce</guid>
      <description>&lt;p&gt;Update: 1-Aug-2019: The example mentioned in this post is now redundant. Use iTerm's &lt;a href="https://www.iterm2.com/3.3/documentation-status-bar.html" rel="noopener noreferrer"&gt;status bar&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.iterm2.com/" rel="noopener noreferrer"&gt;iTerm2&lt;/a&gt; is a very powerful replacement to the built-in terminal. You can read more about why &lt;a href="https://dev.to/t/iterm2"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post will focus on one specific functionality I recently explored and found useful: &lt;a href="https://www.iterm2.com/documentation-badges.html" rel="noopener noreferrer"&gt;Badges&lt;/a&gt; and is meant for beginners unfamiliar with iTerm2&lt;/p&gt;

&lt;h2&gt;
  
  
  Hello World
&lt;/h2&gt;

&lt;p&gt;Let's start with a "Hello World" example.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to "iTerm2 &amp;gt; Preferences &amp;gt; Profiles"&lt;/li&gt;
&lt;li&gt;Add the text "HelloWorld" to the input "General -&amp;gt; Basics -&amp;gt; Badge"&lt;/li&gt;
&lt;/ul&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%2F1xht6w5vlpqo16e5y9cx.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%2F1xht6w5vlpqo16e5y9cx.png" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you open up a new iTerm2 window, you should now see this:&lt;/li&gt;
&lt;/ul&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%2F4z5ylvla766y2blepf62.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%2F4z5ylvla766y2blepf62.png" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;This badge will be displayed in each tab. Split the window into two tabs and this is what you'll see. &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%2Fahtgfk35fgb8pyn51sb9.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%2Fahtgfk35fgb8pyn51sb9.png" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now lets make the badge display something useful like the name of the user and the machine. To do this, we'll first need to install &lt;a href="https://www.iterm2.com/documentation-shell-integration.html" rel="noopener noreferrer"&gt;Shell Integration&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%2Fleqr0d6yknosgoyhwf9i.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%2Fleqr0d6yknosgoyhwf9i.png" width="564" height="920"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now, go back to the Preferences panel and change the Badge to &lt;code&gt;\(session.username)@\(session.hostname)&lt;/code&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%2Fkl0bs1p01b9vv1t1t44q.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%2Fkl0bs1p01b9vv1t1t44q.png" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;iTerm will now display a more useful badge &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%2Fdxz7p7teszg8c1bj8lit.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%2Fdxz7p7teszg8c1bj8lit.png" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Important&lt;/em&gt;: The "Install Shell Integration" step will have to be done on &lt;em&gt;each&lt;/em&gt; host you ssh into before you'll see the correct label.&lt;/p&gt;

&lt;p&gt;Enjoy,&lt;br&gt;
Vivek.&lt;/p&gt;

&lt;p&gt;P.S: Badges also support more advanced features like user-defined variables. Experiment with them to get the look-and-feel you need. All you need to know is some basic bash scripting&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.iterm2.com/" rel="noopener noreferrer"&gt;iTerm2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.iterm2.com/documentation-badges.html" rel="noopener noreferrer"&gt;iTerm2 Badges&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.iterm2.com/documentation-shell-integration.html" rel="noopener noreferrer"&gt;iTerm2 Shell Integration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>iterm2</category>
      <category>terminal</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
