<?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: Oryan Moshe</title>
    <description>The latest articles on DEV Community by Oryan Moshe (@oryanmoshe).</description>
    <link>https://dev.to/oryanmoshe</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%2F194001%2F4e34f41d-4a59-4cb7-8643-e8ed6b8d97c5.jpg</url>
      <title>DEV Community: Oryan Moshe</title>
      <link>https://dev.to/oryanmoshe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/oryanmoshe"/>
    <language>en</language>
    <item>
      <title>The Secret Behind My Latest Blog Post: ChatGPT Did the Heavy Lifting</title>
      <dc:creator>Oryan Moshe</dc:creator>
      <pubDate>Mon, 08 May 2023 14:46:40 +0000</pubDate>
      <link>https://dev.to/oryanmoshe/the-secret-behind-my-latest-blog-post-chatgpt-did-the-heavy-lifting-1jmg</link>
      <guid>https://dev.to/oryanmoshe/the-secret-behind-my-latest-blog-post-chatgpt-did-the-heavy-lifting-1jmg</guid>
      <description>&lt;p&gt;Have you ever wondered how you could streamline your content creation process? Look no further! In this blog post, I'll reveal the secret behind &lt;a href="https://dev.to/oryanmoshe/mock-data-stubs-fake-it-till-you-make-it-24jo"&gt;my latest article&lt;/a&gt;: using &lt;a href="https://openai.com/blog/chatgpt"&gt;OpenAI's ChatGPT&lt;/a&gt; to do the heavy lifting for me.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/oryanmoshe" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0Re-qFsp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/practicaldev/image/fetch/s--VZYG0Tki--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/194001/4e34f41d-4a59-4cb7-8643-e8ed6b8d97c5.jpg" alt="oryanmoshe"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/oryanmoshe/mock-data-stubs-fake-it-till-you-make-it-24jo" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Mock Data &amp;amp; Stubs (Fake it till you make it)&lt;/h2&gt;
      &lt;h3&gt;Oryan Moshe ・ May 7 ・ 11 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#programming&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#career&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#coding&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;In this article, I'll explain how working iteratively with ChatGPT transformed my content creation process and made it more efficient than ever before.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why the Iterative Method Outshines Traditional Prompting
&lt;/h2&gt;

&lt;p&gt;ChatGPT, specifically GPT-4, is a powerful AI language model capable of generating human-like text. Most people use it by giving it a single prompt and waiting for the generated text. However, this "one-shot" approach often results in content that isn't quite what they were hoping for. That's where the iterative method comes in.&lt;/p&gt;

&lt;p&gt;The iterative method is a more collaborative approach, involving working with ChatGPT step-by-step. It allows for continuous feedback and improvement, resulting in higher quality content that aligns better with your vision. In the following sections, I'll detail my journey of teaching ChatGPT my writing style, refining its understanding of the subject, and incorporating helpful prompts to create a well-structured, coherent blog post.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xhRdVbl6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ywsnlh6vz3xhfzh5e2l0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xhRdVbl6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ywsnlh6vz3xhfzh5e2l0.png" alt="A person and a robot working together on a laptop" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Teaching ChatGPT Your Writing Style
&lt;/h2&gt;

&lt;p&gt;For a more personalized and natural-sounding article, it's essential to teach ChatGPT your unique writing style. To do this, you can provide the AI with examples of your previous work. The more examples you give, the better ChatGPT will be able to emulate your voice.&lt;/p&gt;

&lt;p&gt;In our case, I instructed ChatGPT to request more example articles until it felt confident that it could mimic my writing style. I also provided the context that the AI is an engineer who loves to write technical blog posts.&lt;/p&gt;

&lt;p&gt;Here's a simple process for teaching ChatGPT your style:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Gather a few samples of your writing that you feel accurately represent your "voice" (writing style)&lt;/li&gt;
&lt;li&gt;Provide these samples as context to ChatGPT at the beginning of the conversation.&lt;/li&gt;
&lt;li&gt;Instruct ChatGPT to request more examples if it needs them to better understand your writing style.&lt;/li&gt;
&lt;li&gt;Throughout the iterative process, give feedback on the generated content, pointing out where the AI succeeded or missed the mark in capturing your style.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By consistently providing feedback and guidance, you'll help ChatGPT to fine-tune its understanding of your writing style, and the generated content will begin to sound more like you.&lt;/p&gt;




&lt;h2&gt;
  
  
  Instructing ChatGPT to Ask Questions for Better Understanding
&lt;/h2&gt;

&lt;p&gt;Another key aspect of our iterative approach to working with ChatGPT is getting the AI to ask questions for better understanding. Instead of relying solely on your initial input, you can encourage ChatGPT to seek clarification and gather more information about the topic.&lt;/p&gt;

&lt;p&gt;This strategy not only helps the AI produce more accurate and relevant content but also fosters a more interactive and collaborative experience between you and ChatGPT. &lt;/p&gt;

&lt;p&gt;To instruct ChatGPT to ask questions, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In your initial conversation, mention that you want ChatGPT to ask questions if it needs more information or clarification.&lt;/li&gt;
&lt;li&gt;As you work through the iterative process, provide the AI with answers to its questions and use this information to refine the generated content.&lt;/li&gt;
&lt;li&gt;Encourage ChatGPT to continue asking questions if it's still unclear about certain aspects of the topic or your preferences.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By engaging in a back-and-forth dialogue with ChatGPT, you'll create a more comprehensive understanding of the subject matter, resulting in a higher quality article.&lt;/p&gt;




&lt;h2&gt;
  
  
  Orchestrating ChatGPT, Midjourney, and Grammarly Together
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sQLIN-E8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kv8u83sunlkqdbp2f05u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sQLIN-E8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kv8u83sunlkqdbp2f05u.png" alt="The perfect trio" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As with any content writing team, one person can't do it all. We leverage the power of several tools to create the perfect writing team.&lt;/p&gt;

&lt;h3&gt;
  
  
  Improving Prompt Clarity with Grammarly
&lt;/h3&gt;

&lt;p&gt;To ensure that ChatGPT receives the best prompts possible and works more effectively, we use &lt;a href="https://www.grammarly.com/"&gt;Grammarly&lt;/a&gt;, an AI-powered writing assistant, to improve the clarity of our prompts before sending them to ChatGPT. Remember, garbage in - garbage out.&lt;/p&gt;

&lt;p&gt;Here's how to use Grammarly to improve the clarity of your prompts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Grammarly's editor.&lt;/li&gt;
&lt;li&gt;Write your prompt intended for ChatGPT directly in the editor.&lt;/li&gt;
&lt;li&gt;Review and apply Grammarly's suggestions to enhance the clarity and quality of your prompt.&lt;/li&gt;
&lt;li&gt;Keep a history of your prompts in Grammarly for future reference.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By refining your prompts with Grammarly, you ensure that ChatGPT has a clear understanding of your requirements and can generate better results.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating Midjourney Prompts
&lt;/h3&gt;

&lt;p&gt;ChatGPT can be used effectively for generating prompts for other AI tools, such as Midjourney. &lt;a href="https://www.midjourney.com/"&gt;Midjourney&lt;/a&gt; is an AI image generator that creates custom visuals based on text prompts. &lt;/p&gt;

&lt;p&gt;For many tasks, ChatGPT can create content perfectly on its own. However, ChatGPT sometimes provides invalid URLs for images and cannot be trusted with these tasks. Instead, I instructed ChatGPT to generate a description of the image using a "PIC-PROMPT " whenever an image is necessary. Then, I can use Midjourney or search online to find the perfect visual based on the description provided by ChatGPT.&lt;/p&gt;




&lt;h2&gt;
  
  
  Practical Prompt Examples and Tips for Effective ChatGPT Collaboration
&lt;/h2&gt;

&lt;p&gt;In this section, we will go through some practical prompt examples and share useful tips to make your collaboration with ChatGPT more effective.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prompt Examples
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Context&lt;/strong&gt;: When you start out, you need to provide ChatGPT with a context of what it is and what the goals are. Use a prompt like this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a hands-on, highly Pragmatic Programmer who loves to write technical blog posts about your endeavors as a &amp;lt;your role&amp;gt; of a company called &amp;lt;your company&amp;gt;.
Your blog posts are funny and light-hearted but as technical as you can get them while staying relatable.
Your goal is to help me create blog posts about different topics, trying your best to mimic the way I write blog posts.
These articles will be published on dev.to, medium.com, and LinkedIn.

I will feed you a few of my previous blog posts (in markdown format) so you can learn how I usually write.
How many posts are required?

When you're ready, ask me for the first blog-post example.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Content Styling&lt;/strong&gt;: Now that ChatGPT has asked us for our first example, we need to provide it with as much information as possible, while making sure it remembers to keep the feedback loop going. Here is a prompt that worked for me:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I'll now provide the first blog post. After reading it, tell me that you understood it, and then (when you're ready) ask me for the next blog post. If you have learned enough, tell me you don't need another post. When we reach that point, ask me what topic I'd like my next blog post to be about.

The first blog post was published on &amp;lt;website and date&amp;gt;
It has XXX reads, XXX reactions, and XXX comments.
The article's title is: &amp;lt;article title here&amp;gt;
I tagged the article as &amp;lt;tags here&amp;gt;.
Here is the content in markdown format:
&amp;lt;article content here&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Setting the Foundations&lt;/strong&gt;: Eventually, ChatGPT will tell you it has learned enough. At this point, we want &lt;em&gt;it&lt;/em&gt; to drive the process. To do this, we instruct it to ask questions, like so:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ask me up to 10 questions to help guide you to write my next blog posts. The questions can refer to the new blog post topic, specific info needed, or my writing style and goals. So ask me *everything you need* to write my next blog post as best as possible.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Involving ChatGPT&lt;/strong&gt;: Now, ChatGPT will ask you several questions, probably about the topic of your next article, the target audience, format, objectives, etc. Answer these questions, and make sure you keep encouraging ChatGPT to ask you questions and get involved &lt;strong&gt;throughout the whole process&lt;/strong&gt; like so:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Here are the answers to these questions:
&amp;lt;answers here&amp;gt;

Also, the title I had in mind was "&amp;lt;possible title here&amp;gt;". Feel free to suggest another title if you have a better one.

If you feel like you need more information, you can ask me up to 5 more questions (you don't have to use them all, only ask for help if you need it)
Now, we can do this in 1 of 2 ways:
1. We'll write it together incrementally - you'll send a segment of the article, we'll work on it, and then move on to the next iteratively
2. You'll send me a draft, I'll provide feedback, and then you send me a fixed draft, and so on

Which of these 2 approaches do you prefer?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Content Formatting &amp;amp; Triggering Generation&lt;/strong&gt;: Usually, ChatGPT will pick approach 1, but remember you can change these prompts to fit your preference. Now we need to describe how ChatGPT formats the text it generates, and trigger the first generation like so:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sure, we can go with approach 1.
If you have any other questions, please ask me up to 5.
If not, please provide me with the first segment to go over.

From now on, all content related to the article should be formatted as markdown and sent in a code block to allow me to read it.
Add a horizontal line ("---") between the different sections of the post to separate them better.
Whenever you want to represent a code block in the article, instead of using the markdown notation, please wrap the code block with:
^^&amp;lt;syntax language here&amp;gt;^^
...
$$&amp;lt;syntax language here&amp;gt;$$
Instead of images and gifs, add a "PIC-PROMPT &amp;lt;prompt for Midjourney here&amp;gt;" everywhere you deem fit. Midjourney prompts usually start with "/imagine &amp;lt;rest of prompt&amp;gt;".
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Iterate &amp;amp; Improve&lt;/strong&gt;: ChatGPT will send you the first part of your blog post, read it thoroughly (remember, ChatGPT can help with content creation, but we need to manually validate &lt;em&gt;every word&lt;/em&gt;), send feedback for revisions, and once you're happy, tell ChatGPT to move on to the next segment. Remember to &lt;strong&gt;constantly remind ChatGPT it can ask you questions.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tips &amp;amp; Tricks for Effective Collaboration
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Be specific with your prompts&lt;/strong&gt;: Provide clear and concise instructions to help ChatGPT understand what you want.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iterate on the generated content&lt;/strong&gt;: Don't hesitate to revise and refine the content generated by ChatGPT until it meets your expectations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leverage ChatGPT's ability to ask questions&lt;/strong&gt;: Encourage ChatGPT to ask you questions for a better understanding of the subject matter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use external tools like Grammarly&lt;/strong&gt;: Improve the clarity of your prompts using AI-powered writing assistants like Grammarly before sending them to ChatGPT.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understand the limitations&lt;/strong&gt;: Sometimes, some of the content generated is not up to our standards. Usually, we can ask ChatGPT to revise it, but if the fix is easy, it's usually faster to fix it yourself and then tell ChatGPT "Here's the segment I ended up using:" so it keeps learning your writing style. Don't get stuck on trying to make ChatGPT do what you want, if it's faster to just do it yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hone your skill&lt;/strong&gt;: As with any tool, ChatGPT has a learning curve. Refine your skills and learn how to handle unexpected scenarios. For instance, when ChatGPT stops mid-answer, you could simply prompt it with &lt;code&gt;Continue.&lt;/code&gt;, but a more effective prompt might be: &lt;code&gt;You got cut-off, continue from "&amp;lt;cut-off section&amp;gt;". Remember your formatting rules, and keep all content inside of a code block.&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By following these tips and using the practical prompt examples, you can make your collaboration with ChatGPT more effective and efficient, ultimately creating high-quality content for your blog posts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Addressing Common Misconceptions and Concerns
&lt;/h2&gt;

&lt;p&gt;When it comes to using ChatGPT for content creation, some people might have misconceptions or concerns about the process. In this segment, we'll address some of the most common misconceptions and concerns, and help you understand how to overcome them.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Time-intensive process&lt;/strong&gt;: Many people believe that creating blog posts with ChatGPT is time-consuming, but once you've mastered the process of working with the AI, you can actually save time compared to writing everything by yourself. The key is to be patient and invest time in learning how to effectively communicate with and instruct the AI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficulty in forming coherent content&lt;/strong&gt;: Some users might struggle with creating coherent blog posts using ChatGPT because they try to generate the entire post in a single shot, instead of working iteratively with the AI. By following the iterative process that we've outlined in this article, you can ensure that the content is well-structured and coherent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limited creativity&lt;/strong&gt;: AI-generated content might be perceived as lacking creativity or a personal touch. However, by providing specific examples, context, and guidance to ChatGPT, you can infuse your writing style and personality into the generated content. Remember, the more you work with the AI, the better it becomes at understanding your preferences and mimicking your writing style.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependency on AI&lt;/strong&gt;: Some people might worry about becoming too dependent on AI for content creation. While it's true that ChatGPT is a powerful tool, it's essential to strike a balance between AI-assisted writing and your own writing skills. You should always review and revise the content generated by the AI, ensuring that it meets your standards and retains your personal touch.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By addressing these misconceptions and concerns, you can make the most of ChatGPT and leverage its capabilities to create high-quality content for your blog posts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion: Enhancing Your Blogging Process with ChatGPT
&lt;/h2&gt;

&lt;p&gt;Leveraging the power of ChatGPT, you can create compelling and high-quality blog posts that resonate with your readers. By following the tips, tricks, and examples provided in this article, you'll be able to make the most of your collaboration with ChatGPT and streamline your blogging process.&lt;/p&gt;

&lt;p&gt;Remember to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Provide clear and concise prompts to guide the AI.&lt;/li&gt;
&lt;li&gt;Use an iterative approach to create coherent and well-structured content.&lt;/li&gt;
&lt;li&gt;Encourage ChatGPT to ask questions and actively participate in the content creation process.&lt;/li&gt;
&lt;li&gt;Review and revise the generated content to ensure it meets your standards and retains your personal touch.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With patience and practice, you'll master the art of working with ChatGPT to create engaging and informative blog posts that reflect your unique writing style and voice.&lt;/p&gt;

&lt;p&gt;Embrace the power of AI and revolutionize your blogging process with ChatGPT, happy writing!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_HvYyq61--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bi7442wnlcba034kni23.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_HvYyq61--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bi7442wnlcba034kni23.png" alt="ChatGPT as the helping hand behind my articles" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>writing</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Mock Data &amp; Stubs (Fake it till you make it)</title>
      <dc:creator>Oryan Moshe</dc:creator>
      <pubDate>Sun, 07 May 2023 12:00:00 +0000</pubDate>
      <link>https://dev.to/oryanmoshe/mock-data-stubs-fake-it-till-you-make-it-24jo</link>
      <guid>https://dev.to/oryanmoshe/mock-data-stubs-fake-it-till-you-make-it-24jo</guid>
      <description>&lt;h2&gt;
  
  
  Mock Data &amp;amp; Stubs (Fake it till you make it)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---s1UZekz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2wcaux0lolzbwb68kqw9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---s1UZekz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2wcaux0lolzbwb68kqw9.gif" width="800" height="800"&gt;&lt;/a&gt;This is you before implementing mock data to your workflow&lt;/p&gt;

&lt;p&gt;Developing new features can be a total blast, but it can also be a slow and frustrating process. One of the most significant contributors to this frustration is the time it takes to iterate through each development cycle. The longer it takes to develop, test, and refine each feature, the more time is wasted, and the slower progress becomes.&lt;/p&gt;

&lt;p&gt;But what if I told you there's a way to reduce your iteration times significantly? Today, we'll explore a technique that can help you "fake it till you make it" when it comes to developing features: using &lt;strong&gt;mock data, stubs, and fakers&lt;/strong&gt;. This approach will help you eliminate the dependency on external code or resources that aren't necessary at the moment and allow you to focus on the task at hand.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why Reducing Iteration Time is Crucial
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--epx41dCh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lli8lwsaw50786tzt6x3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--epx41dCh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lli8lwsaw50786tzt6x3.png" width="800" height="800"&gt;&lt;/a&gt;Time actually IS money&lt;/p&gt;

&lt;p&gt;Reducing iteration time is crucial for several reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Faster MVP:&lt;/strong&gt; By quickly developing a working Minimum Viable Product (MVP), you can gather real-world feedback and make data-driven decisions about which features are essential and which are not. This way, you can adapt and change your product according to &lt;em&gt;actual&lt;/em&gt; feedback from your experience with the feature, instead of wasting time on unnecessary additions without ensuring the core functionality works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Increased productivity:&lt;/strong&gt; When you spend less time waiting for external dependencies, you can focus on what really matters: writing and refining your code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Greater flexibility:&lt;/strong&gt; When you're not reliant on external code or resources, you can make changes and improvements more easily, allowing you to pivot when necessary and adapt to new requirements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved morale:&lt;/strong&gt; There's nothing worse than being stuck in a never-ending development cycle. By reducing iteration times, you can maintain momentum and keep your team motivated.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that we've covered why it's essential to reduce iteration times, let's dive into what mock data, stubs, and fakers actually are.&lt;/p&gt;




&lt;h3&gt;
  
  
  Mock Data, Stubs, and Fakers: The Building Blocks of Rapid Development
&lt;/h3&gt;

&lt;p&gt;Let's break down these three essential concepts and clarify their differences and interactions.&lt;/p&gt;

&lt;h4&gt;
  
  
  Mock Data
&lt;/h4&gt;

&lt;p&gt;Mock data is a set of fake data points that you can use to simulate the behavior of your application. It helps you avoid relying on real data sources, which can be slow or unavailable during development. By using mock data, you can develop and test your application's logic independently from any external dependencies.&lt;/p&gt;

&lt;h4&gt;
  
  
  Stubs
&lt;/h4&gt;

&lt;p&gt;Stubs are simplified versions of components or functions that return a predetermined response. They help you isolate specific parts of your application and focus on the functionality you're currently developing. Stubs can be used to define the structure of your code, allowing you to move forward without getting stuck on implementing every single function. They're also helpful when collaborating with other team members who are responsible for developing certain functionalities.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fakers
&lt;/h4&gt;

&lt;p&gt;Fakers are libraries or tools that generate realistic-looking data for testing purposes. They can create data that resembles real-world information, such as names, addresses, and dates, without using actual data points.&lt;/p&gt;




&lt;h3&gt;
  
  
  Understanding the Interaction Between Mock Data, Stubs, and Fakers
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DdfIEzvD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3oa30lx1l9idlw4cpfwc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DdfIEzvD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3oa30lx1l9idlw4cpfwc.png" width="800" height="800"&gt;&lt;/a&gt;Essentially all we're doing is connecting empty boxes to other empty boxes&lt;/p&gt;

&lt;p&gt;Now that we know what mock data, stubs, and fakers are, let's discuss how they interact and complement each other in the development process.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mock data represents the data structure and provides a way to simulate application behavior. It is the foundation for building test scenarios and understanding how your application should respond to various inputs.&lt;/li&gt;
&lt;li&gt;Stubs replace parts of your application that depend on external components or services. They help you focus on the functionality you're currently developing by providing predictable behavior.&lt;/li&gt;
&lt;li&gt;Fakers generate realistic data to populate your mock data and stubs. This allows you to create more diverse and complex test scenarios without manually crafting every single data point.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By combining these three techniques, you can create a flexible and efficient development environment that allows you to quickly iterate on your features while focusing on the actual logic at hand.&lt;/p&gt;




&lt;h3&gt;
  
  
  Putting It All Together: A Practical Example
&lt;/h3&gt;

&lt;p&gt;Now we'll look at a practical example of how each of these techniques can be implemented. We chose JavaScript for simplicity, but you can treat this example as pseudo-code for now.&lt;/p&gt;

&lt;p&gt;Let's assume we're building a simple application that displays a list of users and their details. We'll use mock data, stubs, and fakers to create a rapid development environment for this scenario.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Creating Mock Data
&lt;/h4&gt;

&lt;p&gt;First, we need to create some mock data to represent the user information. For this example, we'll define a simple JSON object with user details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"john.doe@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123 Main St, Anytown, USA"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jane Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jane.doe@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"456 Oak St, Anytown, USA"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 2: Implementing Stubs
&lt;/h4&gt;

&lt;p&gt;Now that we have our mock data, we can create a stub function that simulates fetching user data from a server:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;fetchUsers&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="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&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;setTimeout&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;resolve&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John Doe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;john.doe@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;123 Main St, Anytown, USA&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jane Doe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jane.doe@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;456 Oak St, Anytown, USA&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="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;fetchUsers()&lt;/code&gt; function returns a Promise that resolves with the mock data after a 1-second delay. This simulates an asynchronous API call, allowing us to develop and test our application without relying on a real server.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example: Using the &lt;code&gt;fetchUsers()&lt;/code&gt; Stub
&lt;/h4&gt;

&lt;p&gt;To demonstrate using the &lt;code&gt;fetchUsers()&lt;/code&gt; stub function, let's create a simple function that displays the fetched user data:&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;displayUsers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetchUsers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;): &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;displayUsers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example calls the &lt;code&gt;fetchUsers()&lt;/code&gt; stub function and then logs the user details to the console.&lt;/p&gt;

&lt;p&gt;Imagine if we had to wait for a server or a database to respond in order to test our &lt;code&gt;displayUsers()&lt;/code&gt; function, and suddenly the data source is unresponsive, or your internet connection goes out. &lt;br&gt;
Good thing we don't rely on stuff like that anymore (:&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 3: Using Fakers
&lt;/h4&gt;

&lt;p&gt;To make our example more realistic, we can use a faker library to generate a larger set of user data. Here's how we can use the popular &lt;code&gt;Faker.js&lt;/code&gt; library to create a more diverse set of mock users:&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;faker&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;faker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;generateUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;for&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;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findName&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;internet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;streetAddress&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;generateUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our application has a more realistic set of user data to work with, allowing us to test various scenarios and edge cases.&lt;/p&gt;

&lt;p&gt;By combining mock data, stubs, and fakers, we can create a rapid development environment that focuses on the &lt;strong&gt;functionality&lt;/strong&gt; we're currently developing.&lt;/p&gt;




&lt;h3&gt;
  
  
  Best Practices and Potential Pitfalls
&lt;/h3&gt;

&lt;p&gt;Let's dive into some best practices and potential pitfalls you should be aware of while working with these techniques.&lt;/p&gt;

&lt;h4&gt;
  
  
  Keep Your Mock Data Realistic
&lt;/h4&gt;

&lt;p&gt;When creating mock data, it's essential to keep it as realistic as possible. This helps ensure that your tests and development experience closely resemble real-world scenarios. For example, if you're working on a project that deals with user profiles, create mock data that includes a variety of names, ages, and other relevant information, rather than using generic placeholders like "John Doe" or "12345."&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;realisticUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alice@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bob@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Carol&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;carol@example.com&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Don't Overuse Stubs
&lt;/h4&gt;

&lt;p&gt;Stubs can be incredibly useful when it comes to speeding up your development process and isolating different parts of your code. However, it's crucial not to overuse them. Overusing stubs can lead to overly rigid code that's difficult to maintain and refactor. Remember that stubs are a means to an end – they should help you focus on the most critical parts of your code and avoid getting bogged down in implementation details.&lt;/p&gt;

&lt;p&gt;Treat stubs as "empty boxes", when the time comes you still have to fill them up with their contents.&lt;/p&gt;

&lt;h4&gt;
  
  
  Keep Your Fakers Consistent
&lt;/h4&gt;

&lt;p&gt;When using fakers to generate random data, it's essential to maintain consistency across your tests and development environment. Inconsistent faker data can lead to false negatives or positives in your tests, making it challenging to determine whether your code is functioning correctly. To ensure consistency, consider using seed values when initializing your fakers, so the same data is generated every time you run your tests.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;faker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;faker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Seed the random number generator&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// This will always generate the same first name&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// This will always generate the same last name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Popular Libraries and Tools for Mock Data, Stubs, and Fakers
&lt;/h3&gt;

&lt;p&gt;We've discussed the importance of these techniques, and now it's time to see some examples of libraries and tools you can use in different programming languages to help you achieve this. The purpose is to show how these concepts are applicable across various programming environments. However, we'll put more emphasis on C# and Unity3D since these are the main tools used by my team at &lt;a href="https://magnifica.io"&gt;Magnifica VR&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  C# and Unity3D
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;NSubstitute&lt;/strong&gt;: A friendly substitute for .NET mocking libraries, NSubstitute is designed as a more straightforward and more concise alternative to other mocking libraries, making it easier to create substitutes for interfaces and classes in your tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Moq&lt;/strong&gt;: A popular and easy-to-use mocking library for .NET that allows you to create mock objects, set expectations on them, and verify interactions with them in your tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Unity Test Framework&lt;/strong&gt;: Unity3D provides a built-in testing framework that supports unit, integration, and end-to-end testing. It allows you to create test doubles, such as stubs and fakes, to isolate the code under test and remove dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  JavaScript
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Faker.js&lt;/strong&gt;: A popular library for generating fake data, such as names, addresses, and phone numbers. It can be used in combination with stubs to create realistic mock data for your tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sinon.js&lt;/strong&gt;: A library that provides standalone test spies, stubs, and mocks for JavaScript. It works with any unit testing framework and is compatible with different browsers.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Python
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Faker&lt;/strong&gt;: A Python library that generates fake data for you, similar to Faker.js for JavaScript. It's useful for creating mock data for testing or populating databases.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;unittest.mock&lt;/strong&gt;: A built-in library in Python that allows you to create stubs and mocks for your tests. It's easy to use and doesn't require any external dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Ruby
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Faker&lt;/strong&gt;: A Ruby library that generates fake data, such as names, addresses, and phone numbers. It's helpful for creating realistic mock data for your tests or seeding your database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;RSpec Mocks&lt;/strong&gt;: A library that provides a simple and flexible mocking framework for Ruby. It's part of the RSpec testing ecosystem and can be used with other RSpec libraries or as a standalone mocking solution.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see, no matter what programming language you work with, there are libraries and tools available to help you create mock data, stubs, and fakers. Utilizing these tools will save you time and make your testing process more efficient.&lt;/p&gt;

&lt;p&gt;Now that we've explored the available tools and libraries, let's see how all these concepts can be applied in a real-world scenario.&lt;/p&gt;




&lt;h3&gt;
  
  
  Case Study: Implementing Mock Data, Stubs, and Fakers in a Unity3D Project
&lt;/h3&gt;

&lt;p&gt;Let's explore a simple case study in a Unity3D project. We'll create a simple game that displays a list of players with their scores, and we'll use the concepts we have just discussed to speed up the development process and make it more efficient.&lt;/p&gt;

&lt;h4&gt;
  
  
  Setting Up the Project
&lt;/h4&gt;

&lt;p&gt;To start, let's create a new Unity3D project and set up the basic structure. We'll need a script to handle the player data and another one to display it on the screen.&lt;/p&gt;

&lt;p&gt;Create a new C# script called &lt;code&gt;PlayerData.cs&lt;/code&gt; and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlayerData&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Score&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 simple class will hold the player's name and score. Now, let's create another script called &lt;code&gt;PlayerListDisplay.cs&lt;/code&gt; to display the list of players on the screen. In this script, we'll need a function that takes a list of &lt;code&gt;PlayerData&lt;/code&gt; objects and displays them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlayerListDisplay&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DisplayPlayers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlayerData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PlayerData&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Score&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Creating a Stub for Player Data
&lt;/h4&gt;

&lt;p&gt;At this point, we need some data to display on the screen. In a real-world scenario, we might retrieve this data from a server or a database. However, we don't want to depend on an external source while developing and testing our display function. Instead, let's create a stub that generates a list of fake players with random scores.&lt;/p&gt;

&lt;p&gt;Create a new C# script called &lt;code&gt;PlayerDataStub.cs&lt;/code&gt; and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlayerDataStub&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlayerData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GenerateFakePlayers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlayerData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fakePlayers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlayerData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;PlayerData&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;PlayerData&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"Player &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Score&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;

            &lt;span class="n"&gt;fakePlayers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fakePlayers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this stub in place, we can generate a list of fake players to test our &lt;code&gt;DisplayPlayers&lt;/code&gt; function without relying on external data sources.&lt;/p&gt;

&lt;h4&gt;
  
  
  Testing the Display Function
&lt;/h4&gt;

&lt;p&gt;Now, let's test the &lt;code&gt;DisplayPlayers&lt;/code&gt; function by providing it with the fake data generated by our stub. Modify the &lt;code&gt;PlayerListDisplay.cs&lt;/code&gt; script as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlayerListDisplay&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MonoBehaviour&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;PlayerDataStub&lt;/span&gt; &lt;span class="n"&gt;_playerDataStub&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_playerDataStub&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PlayerDataStub&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlayerData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fakePlayers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_playerDataStub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GenerateFakePlayers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;DisplayPlayers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fakePlayers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DisplayPlayers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlayerData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PlayerData&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Score&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this setup, we can quickly test and iterate on our &lt;code&gt;DisplayPlayers&lt;/code&gt; function without worrying about external dependencies.&lt;/p&gt;

&lt;h4&gt;
  
  
  Introducing a Faker for More Realistic Data
&lt;/h4&gt;

&lt;p&gt;While our stub generates fake player data, the names and scores might not be very realistic. To improve the quality of our test data, we can introduce a faker library like &lt;code&gt;Bogus&lt;/code&gt;. This library can generate more realistic names and other data, making our tests more representative of real-world scenarios.&lt;/p&gt;

&lt;p&gt;First, add the &lt;code&gt;Bogus&lt;/code&gt; package to your Unity3D project using the package manager. Then, modify the &lt;code&gt;PlayerDataStub.cs&lt;/code&gt; script to use the &lt;code&gt;Bogus&lt;/code&gt; library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Bogus&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlayerDataStub&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Faker&lt;/span&gt; &lt;span class="n"&gt;_faker&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PlayerDataStub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_faker&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Faker&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlayerData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GenerateFakePlayers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlayerData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fakePlayers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PlayerData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;PlayerData&lt;/span&gt; &lt;span class="n"&gt;player&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;PlayerData&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;Score&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_faker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;

            &lt;span class="n"&gt;fakePlayers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fakePlayers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our stub generates more realistic player data, allowing us to test our &lt;code&gt;DisplayPlayers&lt;/code&gt; function under more accurate conditions.&lt;/p&gt;

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

&lt;p&gt;In this case study, we demonstrated how to use mock data, stubs, and fakers in a Unity3D project. By creating a stub for player data and using a faker library to generate more realistic test data, we were able to quickly iterate on our &lt;code&gt;DisplayPlayers&lt;/code&gt; function without relying on external data sources.&lt;/p&gt;

&lt;p&gt;Implementing these techniques in your projects can help you save time and reduce dependencies, allowing you to focus on building the core functionality of your application.&lt;/p&gt;




&lt;h3&gt;
  
  
  Wrapping Up
&lt;/h3&gt;

&lt;p&gt;Throughout this post, we've discussed the importance of reducing iteration times when developing new features. We've looked at how using mock data, stubs, and fakers can help you achieve that by minimizing dependencies on external data sources or code that's not yet implemented.&lt;/p&gt;

&lt;p&gt;By understanding and applying these techniques, you can focus on building the core functionality of your application, iterate faster, and adapt to real feedback sooner. Remember, it's essential to have a working MVP as fast as possible, so you can modify and improve your product based on actual experience and feedback.&lt;/p&gt;

&lt;p&gt;Don't be afraid to fake it till you make it, and happy coding!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iLUP3WRt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2a6f2il6vkpt7610iiru.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iLUP3WRt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2a6f2il6vkpt7610iiru.png" width="800" height="800"&gt;&lt;/a&gt;Remember to create your boxes and fill them later, not the other way around&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>programming</category>
      <category>career</category>
      <category>coding</category>
    </item>
    <item>
      <title>Debezium Custom Converters - TimestampConverter</title>
      <dc:creator>Oryan Moshe</dc:creator>
      <pubDate>Tue, 26 May 2020 09:37:29 +0000</pubDate>
      <link>https://dev.to/oryanmoshe/debezium-custom-converters-timestampconverter-26hh</link>
      <guid>https://dev.to/oryanmoshe/debezium-custom-converters-timestampconverter-26hh</guid>
      <description>&lt;h1&gt;
  
  
  Debezium Custom Converters
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Creating custom converters using Debezium's new SPI to override value conversions&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Background about the &lt;code&gt;TimestampConverter&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Hey, my name is Oryan Moshe, and I started my own community for senior developers in Israel named &lt;a href="https://in.dev/"&gt;in.dev&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
I'm also a Lead Architect at &lt;a href="https://rivery.io/"&gt;Rivery&lt;/a&gt;, We are a fully managed data integration platform, and part of my job here is developing the ability to stream changes straight from the clients’ databases, to our platform, using change data capture (CDC).&lt;/p&gt;

&lt;p&gt;The last time I coded in Java was 7 years ago, so if you have any suggestions to improve the code shown here please feel free to comment below!&lt;/p&gt;

&lt;p&gt;You can find the converter right here:&lt;br&gt;
&lt;a href="https://github.com/oryanmoshe/debezium-timestamp-converter/"&gt;https://github.com/oryanmoshe/debezium-timestamp-converter/&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  CDC — Change Data Capture
&lt;/h3&gt;

&lt;p&gt;Before we talk about Debezium, we have to talk about CDC.&lt;/p&gt;

&lt;p&gt;CDC is a way for us to get the &lt;em&gt;changes&lt;/em&gt; happening in the database (as opposed to the &lt;em&gt;actual data&lt;/em&gt;)&lt;br&gt;&lt;br&gt;
This means we can actually get &lt;em&gt;every state&lt;/em&gt; that &lt;em&gt;every record&lt;/em&gt; has been through in the database.&lt;/p&gt;

&lt;p&gt;CDC is useful for a number of cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compiling a log of record changes&lt;/li&gt;
&lt;li&gt;Undoing (or reverting) a change&lt;/li&gt;
&lt;li&gt;Tracking record deletion (which is not simply a matter of using &lt;code&gt;SELECT&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  What is Debezium Anyway?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://debezium.io"&gt;Debezium&lt;/a&gt; is an open source platform, maintained by Red Hat, that allows developers to implement CDC into a Kafka infrastructure.&lt;br&gt;&lt;br&gt;
Debezium actuates CDC by configuring connections using the provided Kafka Connect data connectors. Currently there's support for MySQL, PostgreSQL, Microsoft SQL Server, MongoDB, and even some limited support for Oracle.&lt;/p&gt;
&lt;h3&gt;
  
  
  What are converters, and why would we need a custom one?
&lt;/h3&gt;

&lt;p&gt;All messages produced by Debezium are processed before entering the designated topic.&lt;br&gt;&lt;br&gt;
This ensures that all fields of a given type (defined by the schema) behave the same.&lt;br&gt;&lt;br&gt;
In other words, &lt;em&gt;all&lt;/em&gt; &lt;code&gt;DATE&lt;/code&gt; fields on &lt;em&gt;all&lt;/em&gt; of the databases will be transformed into the same format. This is, by default "Days since epoch".&lt;br&gt;&lt;br&gt;
But this behavior isn't always wanted, especially in this temporal example.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;our&lt;/strong&gt; particular use case we need all temporal fields to be in the same format, whether the type is &lt;code&gt;DATE&lt;/code&gt;, &lt;code&gt;DATETIME&lt;/code&gt;, &lt;code&gt;DATETIME2&lt;/code&gt;, &lt;code&gt;TIME&lt;/code&gt; or &lt;code&gt;TIMESTAMP&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
The format we chose was &lt;code&gt;YYYY-MM-dd'T'HH:mm:ss.SSS'Z'&lt;/code&gt;.  &lt;/p&gt;


&lt;h2&gt;
  
  
  Creating a custom converter
&lt;/h2&gt;

&lt;p&gt;Here's an explanation for each step needed to create our &lt;code&gt;TimestampConverter&lt;/code&gt;.  &lt;/p&gt;
&lt;h3&gt;
  
  
  The basics of custom converters
&lt;/h3&gt;

&lt;p&gt;To allow such behavior, the Debezium SPI (Service Provider Interface) was added to Debezium Version 1.1.&lt;br&gt;&lt;br&gt;
This allows developers to make their own converters with Java, by creating a class that implements the &lt;code&gt;io.debezium.spi.converter.CustomConverter&lt;/code&gt; interface.&lt;/p&gt;
&lt;h3&gt;
  
  
  The First Gotcha
&lt;/h3&gt;

&lt;p&gt;What we didn't know when we started developing this converter, is that once we registered a custom converter to a temporal column, Debezium's behavior became sporadic. Sometimes it'll pass a &lt;code&gt;DATE&lt;/code&gt; column as "Days since epoch", as expected, but sometimes it'll pass it as a string, matching the date format of the database it came from.&lt;/p&gt;

&lt;p&gt;This meant we had to have all of our bases covered, both for numeric values (let's say, "Days since epoch") and for all date format databases can produce (&lt;code&gt;YYYY-MM-dd&lt;/code&gt;, &lt;code&gt;dd/MM/YYYY&lt;/code&gt;, &lt;code&gt;YYYY-MMM-dd&lt;/code&gt;, etc.)&lt;/p&gt;

&lt;p&gt;Things got &lt;em&gt;a bit&lt;/em&gt; complicated on the logic front, but let's not get into this right now.&lt;/p&gt;
&lt;h3&gt;
  
  
  What's needed for our custom converter to work
&lt;/h3&gt;

&lt;p&gt;Each converter has to implement at least two methods to be harnessed by Debezium:  &lt;/p&gt;
&lt;h4&gt;
  
  
  configure
&lt;/h4&gt;

&lt;p&gt;This method runs when the connector is initialised. It accepts one argument:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;props&lt;/strong&gt; &lt;br&gt;
An object of type &lt;code&gt;java.util.Properties&lt;/code&gt;, containing all of the properties we passed to our converter instance.&lt;/p&gt;
&lt;h4&gt;
  
  
  converterFor
&lt;/h4&gt;

&lt;p&gt;This method runs once for each column defined in our schema, and its job is to define (a.k.a "register") the converter for each. It accepts two arguments:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;column&lt;/strong&gt; &lt;br&gt;
An object of type &lt;code&gt;io.debezium.spi.converter.RelationalColumn&lt;/code&gt;, containing the definition of the column we're currently handling, including its name, type, size, table, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;registration&lt;/strong&gt; &lt;br&gt;
An object of type &lt;code&gt;io.debezium.spi.converter.CustomConverter.ConverterRegistration&lt;/code&gt;, an internal definition, that has one method: &lt;code&gt;register&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Using the &lt;code&gt;configure&lt;/code&gt; method
&lt;/h3&gt;

&lt;p&gt;As stated above, we use the &lt;code&gt;configure&lt;/code&gt; method to pass properties into our converter. This is important because &lt;strong&gt;we can use the same converter for multiple connectors&lt;/strong&gt;, and change its behavior according to these properties.  &lt;/p&gt;

&lt;p&gt;For our &lt;code&gt;TimestampConverter&lt;/code&gt; we wanted to pass four properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;debug&lt;/code&gt; – Indicates whether to print debug messages. Defaults to &lt;code&gt;false&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;format.date&lt;/code&gt; – The format to convert all columns of type &lt;code&gt;DATE&lt;/code&gt;. Defaults to &lt;code&gt;YYYY-MM-dd&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;format.time&lt;/code&gt; – The format to convert all columns of type &lt;code&gt;TIME&lt;/code&gt;. Defaults to &lt;code&gt;HH:mm:ss&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;format.datetime&lt;/code&gt; – The format to convert &lt;strong&gt;all other temporal columns&lt;/strong&gt;. Defaults to &lt;code&gt;YYYY-MM-dd'T'HH:mm:ss.SSS'Z'&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these properties are optional and have default values associated with them.&lt;br&gt;&lt;br&gt;
To support them we defined each of them as a class property with the default value. Inside the &lt;code&gt;configure&lt;/code&gt; method we assigned them with the passed value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TimestampConverter&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;CustomConverter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SchemaBuilder&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RelationalColumn&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;DEFAULT_DATETIME_FORMAT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;DEFAULT_DATE_FORMAT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"YYYY-MM-dd"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;DEFAULT_TIME_FORMAT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"HH:mm:ss.SSS"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;strDatetimeFormat&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strDateFormat&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strTimeFormat&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;SimpleDateFormat&lt;/span&gt; &lt;span class="n"&gt;simpleDatetimeFormatter&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;simpleDateFormatter&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;simpleTimeFormatter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Properties&lt;/span&gt; &lt;span class="n"&gt;props&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;strDatetimeFormat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;props&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"format.datetime"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;DEFAULT_DATETIME_FORMAT&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;simpleDatetimeFormatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SimpleDateFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;strDatetimeFormat&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;strDateFormat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;props&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"format.date"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;DEFAULT_DATE_FORMAT&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;simpleDateFormatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SimpleDateFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;strDateFormat&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;strTimeFormat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;props&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"format.time"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;DEFAULT_TIME_FORMAT&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;simpleTimeFormatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SimpleDateFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;strTimeFormat&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;debug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;props&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"debug"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;simpleDatetimeFormatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTimeZone&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TimeZone&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTimeZone&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UTC"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;simpleTimeFormatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTimeZone&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TimeZone&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTimeZone&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UTC"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using the &lt;code&gt;converterFor&lt;/code&gt; method
&lt;/h3&gt;

&lt;p&gt;Now it's time for the big moment. Each column must be converted to its respective format.&lt;/p&gt;

&lt;p&gt;First of all, we have to understand the type of the column we're currently handling. This is determined using &lt;code&gt;column.typeName&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
If the type is any of the temporal types (defined as a class constant) we handle it accordingly. If it's not, we do nothing, and Debezium will take control.&lt;/p&gt;

&lt;p&gt;To tell Debezium to convert a specific column to something else, we need to use the &lt;code&gt;registration&lt;/code&gt; passed to us. Then &lt;code&gt;register&lt;/code&gt; it, providing a &lt;code&gt;schema&lt;/code&gt; (create one of type &lt;code&gt;string&lt;/code&gt; and make it &lt;code&gt;optional&lt;/code&gt;) and a converter.&lt;/p&gt;

&lt;p&gt;The converter is just a function, or in our case a lambda, that receives an &lt;code&gt;Object&lt;/code&gt;. This is the source value, and returns a value matching the schema we provided. In our case, we needed to return a &lt;code&gt;String&lt;/code&gt; (or &lt;code&gt;null&lt;/code&gt;, because we made it &lt;code&gt;optional&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;converterFor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RelationalColumn&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ConverterRegistration&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SchemaBuilder&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SUPPORTED_DATA_TYPES&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;anyMatch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;typeName&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;())))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;isTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"time"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;typeName&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetimeSchema&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rawValue&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rawValue&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rawValue&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

            &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;millis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getMillis&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rawValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;isTime&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

            &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;instant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofEpochMilli&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;millis&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;Date&lt;/span&gt; &lt;span class="n"&gt;dateObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instant&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;typeName&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"time"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;simpleTimeFormatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dateObject&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"date"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;simpleDateFormatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dateObject&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;simpleDatetimeFormatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dateObject&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;});&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this code snippet look at the two crucial parts we have mentioned before. These are the call to &lt;code&gt;registration.register&lt;/code&gt;, and the &lt;code&gt;return&lt;/code&gt; statements.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Using a Custom Converter with Debezium
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;Installation in our Debezium cluster is straight-forward. We just need to add the &lt;code&gt;.jar&lt;/code&gt; file of the converter to the connector we want to use it in.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Second Gotcha
&lt;/h4&gt;

&lt;p&gt;Notice I said " ... to the connecter we want ... ", this is a thing Debezium didn't make clear in the documentation. We need to add this converter to &lt;strong&gt;every connector&lt;/strong&gt; if we want to use it in.&lt;br&gt;&lt;br&gt;
Let's say the base folder for connectors is &lt;code&gt;/kafka/connect&lt;/code&gt;. Then inside we'll find folders like &lt;code&gt;debezium-connector-mysql&lt;/code&gt;, or &lt;code&gt;debezium-connector-postgres&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We need to add the converter &lt;code&gt;.jar&lt;/code&gt; file to each of those folders if we intend to use it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;p&gt;After adding the &lt;code&gt;.jar&lt;/code&gt; file to our connector, we can configure our connectors to use it!&lt;/p&gt;

&lt;p&gt;To do so all we need to do is add the following keys to our existing configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"converters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"timestampConverter"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"timestampConverter.type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"oryanmoshe.kafka.connect.util.TimestampConverter"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we need to customize the formats of specific data types, we can use these additional configuration keys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"timestampConverter.format.time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HH:mm:ss.SSS"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"timestampConverter.format.date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YYYY-MM-dd"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"timestampConverter.format.datetime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YYYY-MM-dd'T'HH:mm:ss.SSS'Z'"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"timestampConverter.debug"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"false"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;The addition of an SPI to Debezium brought a lot to the table in term of custom converters.&lt;br&gt;&lt;br&gt;
This allows us to get a tailored fit CDC connector, with the data streaming into our Kafka cluster exactly in the format we want.&lt;/p&gt;

&lt;p&gt;I didn't include the actual logic, converting the values from their raw format into the epoch time (this part is contained in the &lt;code&gt;getMillis&lt;/code&gt; method)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;But&lt;/strong&gt; I have published the &lt;code&gt;TimestampConverter&lt;/code&gt; as open source, so anyone can read the code there, use the converter in an application (be it as a &lt;code&gt;.jar&lt;/code&gt; file found in the releases section, or as a dependency found in the packages section), or contribute to its development!&lt;/p&gt;

&lt;p&gt;Feel free to suggest contributions to this converter, and share with me what kind of converters &lt;em&gt;you&lt;/em&gt; created using the new Debezium SPI, and which ones you wish were made!&lt;/p&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;p&gt;To read more about Debezium Custom Converter visit their official documentation:&lt;br&gt;
&lt;a href="https://debezium.io/documentation/reference/1.1/development/converters.html"&gt;https://debezium.io/documentation/reference/1.1/development/converters.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Link to the repository of my &lt;code&gt;TimestampConverter&lt;/code&gt;:&lt;br&gt;
&lt;a href="https://github.com/oryanmoshe/debezium-timestamp-converter"&gt;https://github.com/oryanmoshe/debezium-timestamp-converter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>debezium</category>
      <category>cdc</category>
      <category>java</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Ruby Compact Internals</title>
      <dc:creator>Oryan Moshe</dc:creator>
      <pubDate>Sun, 11 Aug 2019 23:25:04 +0000</pubDate>
      <link>https://dev.to/oryanmoshe/ruby-compact-array-of-hashes-19ho</link>
      <guid>https://dev.to/oryanmoshe/ruby-compact-array-of-hashes-19ho</guid>
      <description>&lt;p&gt;I encountered a weird phenomenon regarding ruby's &lt;code&gt;compact&lt;/code&gt; method, and I thought I'd share my findings with you.&lt;br&gt;
When working on "The Grid" dashboard here at &lt;a href="https://monday.com" rel="noopener noreferrer"&gt;monday.com&lt;/a&gt;, I got to a point where I had an array of hashes, and some of the hashes were empty.&lt;br&gt;
I needed to get rid of those.&lt;/p&gt;


&lt;h2&gt;
  
  
  Bottom line first
&lt;/h2&gt;

&lt;p&gt;To get the result I wanted I just did &lt;code&gt;a.reject { |v| v.blank? }&lt;/code&gt; instead of &lt;code&gt;a.compact&lt;/code&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Expectation VS Reality.
&lt;/h2&gt;

&lt;p&gt;When I used &lt;code&gt;compact&lt;/code&gt; on the array I was a bit surprised by the result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="ss"&gt;key: &lt;/span&gt;&lt;span class="s1"&gt;'value'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;key: &lt;/span&gt;&lt;span class="s1"&gt;'value2'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{}]&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compact&lt;/span&gt;
&lt;span class="c1"&gt;#[{ key: 'value' }, { key: 'value2' }, {}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I expected the &lt;code&gt;compact&lt;/code&gt; method to just go over each element in the array, check if it's &lt;code&gt;present?&lt;/code&gt; and if it is return it.&lt;br&gt;
Something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compact&lt;/span&gt;
  &lt;span class="nb"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So when I had an array with empty hashes that wouldn't go away I had to find the explanation.&lt;/p&gt;




&lt;h2&gt;
  
  
  The implementations behind the scenes
&lt;/h2&gt;

&lt;p&gt;As usual, I dug deeper for the implementations.&lt;/p&gt;

&lt;p&gt;From past research I know ruby's Hash also has a &lt;code&gt;compact&lt;/code&gt; method and I know it's pretty straightforward, for each key in the hash we check if it's &lt;code&gt;nil&lt;/code&gt;, if it's not we return it.&lt;br&gt;
Super easy. Similar to what I suggested above, easy to understand, although it wouldn't solve the issue (because we use &lt;code&gt;nil?&lt;/code&gt; instead of &lt;code&gt;blank?&lt;/code&gt; or &lt;code&gt;!present?&lt;/code&gt;)*&lt;/p&gt;

&lt;p&gt;&lt;em&gt;activesupport/lib/active_support/core_ext/hash/compact.rb, line 12&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compact&lt;/span&gt;
  &lt;span class="nb"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In arrays however the implementation is more complex. It is implemented purely in C.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt;
&lt;span class="nf"&gt;rb_ary_compact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;ary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;ary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rb_ary_dup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;rb_ary_compact_bang&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ary&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;h2&gt;
  
  
  C is love, C is life
&lt;/h2&gt;

&lt;p&gt;If you don't have any background in C, this next part might be less interesting to you. I'll try to make it as understandable as possible, but I highly recommend learning C if you have the time!&lt;/p&gt;

&lt;h3&gt;
  
  
  Pointers background
&lt;/h3&gt;

&lt;p&gt;In case you don't know, the way we access our program's memory (the place where the variables are stored) is by using &lt;strong&gt;pointers&lt;/strong&gt;.&lt;br&gt;
These pointers are basically just an address in the memory where we can find the beginning of our variable (each variable takes a different amount of memory, we just get the required number of bytes starting at the pointer's position)&lt;/p&gt;

&lt;p&gt;Arrays work similarly (actually, an array is literally a pointer), let's say we have an array of 3 characters, each character consists of one byte, all 3 bytes are stored one after the other in memory.&lt;br&gt;
We can go to the first address in the array (or the pointer to that array), and take 1 byte, that's the first character. Then we take another, that's the second, and then the third.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sc"&gt;'m'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'i'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'p'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cm"&gt;/*
Behind the scenes it's stored like so:
... 6d 69 70 ...
    ^ arr[0] == *arr
... 6d 69 70 ...
       ^ arr[1] == *(arr + 1)
... 6d 69 70 ...
          ^ arr[2] == *(arr + 2)
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another thing to note: in C, if we have a pointer stored in a variable, let's say &lt;code&gt;ptr&lt;/code&gt;, this variable will contain the address to the value. To get to the value &lt;em&gt;itself&lt;/em&gt; we have to use the &lt;strong&gt;"dereference" operator&lt;/strong&gt; (an asterisk before the variable name) like so &lt;code&gt;*ptr&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Back to code
&lt;/h3&gt;

&lt;p&gt;Above we saw the C implementation of ruby's array &lt;code&gt;compact&lt;/code&gt;, we can see a function that receives an array, duplicates it (using the &lt;code&gt;rb_ary_dup&lt;/code&gt; function), &lt;code&gt;compact&lt;/code&gt;s it using the &lt;code&gt;rb_ary_compact_bang&lt;/code&gt; function ("bang" is the same as the "!" symbol, so it basically runs &lt;code&gt;compact!&lt;/code&gt;), and then returns the duplicated, compacted array.&lt;/p&gt;

&lt;p&gt;To find that &lt;code&gt;rb_ary_compact_bang&lt;/code&gt; function I went to &lt;a href="https://github.com/ruby/ruby/blob/9d298b9dab831f966ea4bf365c712161118dd631/array.c" rel="noopener noreferrer"&gt;ruby's source code&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;ruby/array.c, line 5022&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt;
&lt;span class="nf"&gt;rb_ary_compact_bang&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="n"&gt;ary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;rb_ary_modify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;RARRAY_CONST_PTR_TRANSIENT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* WB: no new reference */&lt;/span&gt;
  &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;RARRAY_LEN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NIL_P&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;RARRAY_CONST_PTR_TRANSIENT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RARRAY_LEN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Qnil&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;ary_resize_smaller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ary&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;Don't close the post! This code isn't friendly for people with no experience in C, but most of it is memory management, some games of allocating memory correctly, resizing arrays to prevent memory leaks etc. I can do a deeper dive in a seperate post if anyone wants.&lt;/p&gt;

&lt;p&gt;The interesting part is this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NIL_P&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Line by line
&lt;/h3&gt;

&lt;p&gt;As I explained above, an array is just a set of variables stored one after the other in our program's memory, so we can get a pointer to the first variable, and then just increase it by 1 every time to get to the next one (&lt;code&gt;t++&lt;/code&gt; increases &lt;code&gt;t&lt;/code&gt; by 1)&lt;/p&gt;

&lt;p&gt;What we have here is a loop that goes from the beginning of the array, with our current position in the &lt;code&gt;t&lt;/code&gt; variable (a pointer to the array), and we iterate over every element until we reach the &lt;code&gt;end&lt;/code&gt; of the array (the address of the beginning of the array + the length of the array)&lt;/p&gt;

&lt;p&gt;Every time, we check whether the current value is &lt;code&gt;nil&lt;/code&gt; (using the &lt;code&gt;NIL_P&lt;/code&gt; macro defined in &lt;em&gt;include/ruby/ruby.h line 482&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;If it is &lt;code&gt;nil&lt;/code&gt; we just increase the pointer, advancing one spot in our array.&lt;br&gt;
If it's not &lt;code&gt;nil&lt;/code&gt; we put the current value from the original &lt;code&gt;t&lt;/code&gt; (remember, the &lt;code&gt;*&lt;/code&gt; dereferences the pointer) into another array we keep stored in the pointer &lt;code&gt;p&lt;/code&gt;, and then advance the pointer one spot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;In a nutshell, it's the same as doing &lt;code&gt;a.reject { |v| v.nil? }&lt;/code&gt; but more memory aware and efficient.&lt;/p&gt;

&lt;p&gt;Just to show the difference in efficiency I benchmarked both ways against an array of 20,000,000 elements with alternating integers and &lt;code&gt;nil&lt;/code&gt;s.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fk9o8p26uqlr0mvuq5l4c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fk9o8p26uqlr0mvuq5l4c.png"&gt;&lt;/a&gt;&lt;br&gt;
As you can see, array's &lt;code&gt;compact&lt;/code&gt; method using C null pointer elimination is 6.6 times faster than the &lt;code&gt;reject&lt;/code&gt; method!&lt;/p&gt;

&lt;p&gt;I hope you enjoyed learning a bit about ruby's internal mechanisms!&lt;/p&gt;

&lt;p&gt;* This is the activesupport implementation of Hash &lt;code&gt;compact&lt;/code&gt;, if you'd like to see the C implementation you can go to &lt;em&gt;&lt;a href="https://github.com/ruby/ruby/blob/4daff3a603d1a8b2656e82108e2f7d0abf8103c9/hash.c" rel="noopener noreferrer"&gt;ruby/hash.c&lt;/a&gt; line 4110&lt;/em&gt;, but it's pretty similar!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>internals</category>
      <category>c</category>
      <category>research</category>
    </item>
    <item>
      <title>"One TV with a number on it can be better than the best of managers."</title>
      <dc:creator>Oryan Moshe</dc:creator>
      <pubDate>Mon, 05 Aug 2019 23:35:15 +0000</pubDate>
      <link>https://dev.to/oryanmoshe/one-tv-with-a-number-on-it-can-be-better-than-the-best-of-managers-2mg3</link>
      <guid>https://dev.to/oryanmoshe/one-tv-with-a-number-on-it-can-be-better-than-the-best-of-managers-2mg3</guid>
      <description>&lt;p&gt;Here at &lt;a href="https://monday.com" rel="noopener noreferrer"&gt;monday.com&lt;/a&gt; we are always excited to show off our dashboards to visitors.&lt;br&gt;
No, seriously, it's like the first thing anyone that comes to our offices sees.&lt;/p&gt;

&lt;p&gt;We have more TVs than we have employees, and we still fight on what to put on each of them.&lt;/p&gt;
&lt;h2&gt;
  
  
  A dashboard? Do you mean that thing in my car?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/BYQkYecpBaa4/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/BYQkYecpBaa4/giphy.gif"&gt;&lt;/a&gt;&lt;br&gt;
So I guess we should begin with what is a dashboard&lt;/p&gt;
&lt;h3&gt;
  
  
  dash·board
&lt;/h3&gt;
&lt;h4&gt;
  
  
  /ˈdaSHbôrd/
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;a graphical summary of various pieces of important information, typically used to give an overview of a business.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing kicks off a good tech post like a bone dry dictionary definition, but the important takeaway from this definition is that it's wrong.&lt;/p&gt;

&lt;p&gt;I mean, it's probably right if you search the dictionary, but it doesn't convey the true meaning of a dashboard.&lt;/p&gt;

&lt;p&gt;Let me try it myself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Important&lt;/strong&gt; information displayed in a &lt;strong&gt;correct&lt;/strong&gt; and &lt;strong&gt;engaging&lt;/strong&gt; way in order to drive people to action.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Much better.&lt;/p&gt;

&lt;p&gt;If the data is not important, then there's no reason to display it, and if it's not displayed correctly it's better not to show it at all, as incorrect data is even worse than no data, and if the data is important and correct, but doesn't engage people or drives them to action, what is the purpose of that dashboard to begin with?&lt;/p&gt;


&lt;h2&gt;
  
  
  All of my data is important!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/l0MYAY18Pxyxwu2xa/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l0MYAY18Pxyxwu2xa/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So how do we define important information? How do we decide what belongs on a dashboard and what can stay in the database?&lt;/p&gt;

&lt;p&gt;We have a term called KPI - &lt;strong&gt;K&lt;/strong&gt;ey &lt;strong&gt;P&lt;/strong&gt;erformance &lt;strong&gt;I&lt;/strong&gt;ndicator.&lt;br&gt;
This is usually a number (or a collection of numbers) that everyone in the company understands, and are able to explain the changes in it over time (whether it improved, got worse, and what affects it in general)&lt;/p&gt;

&lt;p&gt;We have a different KPI for every team and use case. Sometimes it's easy to define, sometimes it's hard and we have to use proxy KPIs (numbers that we know affect our main KPI in indirect way) and sometimes it's just "impossible" to find (more on that later)&lt;/p&gt;
&lt;h3&gt;
  
  
  KPI Engineering
&lt;/h3&gt;

&lt;p&gt;Finding KPIs is a true artform, it takes time to hone that skill.&lt;br&gt;
Some rules of thumb for a good KPI are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It should be measurable&lt;/li&gt;
&lt;li&gt;It should change frequently ("frequent" is a relative term, that varies from one business to another)&lt;/li&gt;
&lt;li&gt;There's a clear indication on which direction is better (should we drive it up or down)&lt;/li&gt;
&lt;li&gt;People in the company should be able to move it&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Qualitaitve VS Quantitative
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/RGqP1iMUmmx0Q7rpmt/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/RGqP1iMUmmx0Q7rpmt/giphy.gif"&gt;&lt;/a&gt;&lt;br&gt;
Sometimes it's simple to measure your KPI (quantitative), for example our Customer Success' team main KPI is their response time, and their goal is to answer a ticket under 10 minutes. Simple enough, just collect the data from Zendesk and calculate the Average response time.&lt;/p&gt;

&lt;p&gt;But sometimes it's not as simple, sometimes your KPI is qualitative, meaning you have to deduce it from people's reaction to your work. This could be from high-touch conversations with them, reviews, or surveys they answered. This kind of KPI is not measured as easily and, as such, is harder to display on a screen. For example, my KPI is to make our marketing team work as efficiently as possible (officially we want to help them do 10 times what they were able to do without us), but it's not something we can measure or calculate, all we can do is sit with them, hear their issues and see if we were able to address them.&lt;/p&gt;
&lt;h3&gt;
  
  
  Proxy KPIs
&lt;/h3&gt;

&lt;p&gt;Some KPIs take time to "mature" - meaning, if we change something in the system, we can only measure them after a maturity period passes.&lt;br&gt;
For example, one of our main KPIs "Conversion to paying" which is basically the percentage of paying accounts out of the total number of accounts.&lt;/p&gt;

&lt;p&gt;At first glance it seems simple enough, but the thing is, monday.com has a trial period of 14 days, and usually accounts don't pay until their trial is up.&lt;/p&gt;

&lt;p&gt;So, if we changed something, and it hurt our conversion rate, we would only know about it after &lt;strong&gt;14 days!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To solve that issue we have to find a "proxy KPI", a metric that matures faster than our main KPI, but has a high correlation to it.&lt;br&gt;
One of those proxies in our specific case is the amount of content created in the system. After only 3 days we can know (with relatively high certainty) whether an account is going to convert into a paying customer or not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep tuned for another post solely dedicated for KPI engineering!&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Correct? Of course my data is correct!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.tenor.com%2Fimages%2Ff90b120cbf8087b350d44df4aa7f69a2%2Ftenor.gif%3Fitemid%3D11815677" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.tenor.com%2Fimages%2Ff90b120cbf8087b350d44df4aa7f69a2%2Ftenor.gif%3Fitemid%3D11815677"&gt;&lt;/a&gt;&lt;br&gt;
Now, I'm sure your data is correct just like ours is! But the way you display your data is just as important as how accurate it actually is.&lt;/p&gt;

&lt;p&gt;I can talk for hours on how to "hack" data and display it in a way where it shows something completely wrong in a very convincing way, but for now I'll settle for 2 examples of data hacking.&lt;/p&gt;

&lt;p&gt;Before starting, it's important to reiterate that if you display your data incorrectly you're lying to yourself and your company, and it makes you think you're in a place you're not. If you don't display your data correctly, you might as well not display it at all.&lt;/p&gt;
&lt;h3&gt;
  
  
  Hiding data
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.tenor.com%2Fimages%2F42c12b7576610e8c6466e8486983fdf7%2Ftenor.gif%3Fitemid%3D3481570" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.tenor.com%2Fimages%2F42c12b7576610e8c6466e8486983fdf7%2Ftenor.gif%3Fitemid%3D3481570"&gt;&lt;/a&gt;&lt;br&gt;
The first, and most naive way to "hack" your data is just to hide the right stuff. Do you have a cohort of accounts that misbehaves? Just remove it from the graph!&lt;br&gt;
Displaying multiple graphs of metrics and your churn graph is on the rise? Just remove that block!&lt;/p&gt;

&lt;p&gt;Doing so will make you look better by hiding the worst things in your data. The problem is it'll hide those things from you as well, and you can't learn from your past mistakes if you bury them underground.&lt;/p&gt;
&lt;h3&gt;
  
  
  Axes scaling
&lt;/h3&gt;

&lt;p&gt;Another, less obvious way to show your data incorrectly is by not locking your axes, the byproduct of which is increased vulnerability to graph manipulation you wouldn't even be aware of.&lt;/p&gt;

&lt;p&gt;If you make your graph narrower, but not shorter, you will put more weight on to your Y axes, making smaller changes in your graph look much more dramatic.&lt;/p&gt;

&lt;p&gt;Just in case I wasn't clear here are two examples, one of an incorrect graph, with scaleable axes, and another of a correct graph in a dashboard, where the axes are locked.&lt;/p&gt;

&lt;p&gt;Here's how not to do it&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Rq4Uu6Q8sQY"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;And here's how to do it&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/0s29pia74Gw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Picking the correct visualization
&lt;/h3&gt;

&lt;p&gt;Another part of displaying your data correctly is picking the correct visualization to display it.&lt;/p&gt;

&lt;p&gt;Not all visualizations work on all kinds of data!&lt;br&gt;
For example, a time series (continuous data where your X axis is time based) should usually be displayed in a line chart.&lt;br&gt;
A stacked time series (where the Y values of multiple series can be summed up to receive a meaningful number) should be displayed in an area chart (just like line chart, but the area under the line is colored as well)&lt;/p&gt;

&lt;p&gt;Categorical data is usually best visualized using a bar chart, but if all categories are a part of a bigger number then you should probably use a pie chart.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.tenor.com%2Fimages%2F41ede2ea0eeb8d2d85ed83e23b36c238%2Ftenor.gif%3Fitemid%3D3336960" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.tenor.com%2Fimages%2F41ede2ea0eeb8d2d85ed83e23b36c238%2Ftenor.gif%3Fitemid%3D3336960"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have more than 1 dimension to display (for example, displaying both the number of visits to a page, and how much they cost you) you should use a bubble chart. Or a heatmap! Speaking of heatmaps, those are also good at displaying a weekly time series and correlation matrices.&lt;/p&gt;

&lt;p&gt;Bottom line, there are so many visualizations, each one fitting a specific use case, and if people don't understand your data at a glance it's a good signal you should rethink your chart types.&lt;/p&gt;




&lt;h2&gt;
  
  
  Engaging? How can numbers be engaging?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.tenor.com%2Fimages%2F6f4e79dd061734038a2c3f84ce16388a%2Ftenor.gif%3Fitemid%3D5099714" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.tenor.com%2Fimages%2F6f4e79dd061734038a2c3f84ce16388a%2Ftenor.gif%3Fitemid%3D5099714"&gt;&lt;/a&gt;&lt;br&gt;
Here's the best thing about creating a dashboard. You have to make people care about it!&lt;br&gt;
Creating an important dashboard, with the correct visualization just isn't enough. I created &lt;em&gt;countless&lt;/em&gt; dashboards that aren't in use just because they weren't engaging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Visual cues
&lt;/h3&gt;

&lt;p&gt;Sometimes all a dashboard has to do to display data in an engaging way is just to find the right way to drive people forwards.&lt;/p&gt;

&lt;p&gt;For example, one of our Customer Success' team main KPIs is having a response time of less than 10 minutes, so everytime there's a ticket in the queue for more than 10 minutes they see it on their dashboard burning in flames.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6ttfkhuno783a8b2800i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6ttfkhuno783a8b2800i.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or "The Grid", which displays both product and technical alerts for company-wide usage, that is usually completely green, but when something turns red it's immediately visible and can be easily inspected using our touch TVs.&lt;br&gt;
This makes people want to keep The Grid green at all times.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Faoabcdsyaq1nqzxj27cv.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Faoabcdsyaq1nqzxj27cv.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or our "Celebrations" overlay, which allows us to display a celebration with an employee picture, confetti and a short message whenever we want (for example, when one of our consultants closes a big deal with a customer.&lt;br&gt;
Everyone wants to celebrate their successes, and that's a nice way to drive people to succeed.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbj122rbsze1tqjmb4myh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fbj122rbsze1tqjmb4myh.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Mobile responsiveness
&lt;/h3&gt;

&lt;p&gt;Don't underestimate the importance of mobile dashboards!&lt;br&gt;
Many people want to know they're current state on their way up the elevator, or between meetings.&lt;/p&gt;

&lt;p&gt;Some people don't even have time to sit by a computer! If your dashboard displays an important metric that people would want to consume often (like "The Grid") it should look as good on mobile as it is on desktop. It also fuels the "addictive" nature of dashboards, which is awesome for you!&lt;/p&gt;

&lt;h3&gt;
  
  
  Sounds
&lt;/h3&gt;

&lt;p&gt;Your TVs probably have speakers built in, and you can use them! Many of our dashboards make sounds whenever something important happens.&lt;br&gt;
There's a "gong" sound when the consulting team closes a deal, music when a customer starts paying, notifications when someone starts deploying to production, or alerts when deployment to production fails.&lt;/p&gt;

&lt;p&gt;It is important not to go over the edge though, as many people started muting the TVs after we started having many paying customers every minute, each making a sound. We had to decrease the frequency of those sounds.&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;In conclusion, dashboards are the thing that drives our company forwards, without them displaying &lt;em&gt;important information in a correct and engaging way&lt;/em&gt;, we wouldn't be able to get to where we are right now, and I think any company that wants to succeeded should be data driven and make sure it has the right dashboards for the job!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.tenor.com%2Fimages%2F0203563fef797baa84ceabfeda1b1066%2Ftenor.gif%3Fitemid%3D11019563" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia1.tenor.com%2Fimages%2F0203563fef797baa84ceabfeda1b1066%2Ftenor.gif%3Fitemid%3D11019563"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>data</category>
      <category>datavis</category>
      <category>dashboard</category>
      <category>dataisbeautiful</category>
    </item>
    <item>
      <title>Replicating PostgresSQL into MemSQL's columnstore</title>
      <dc:creator>Oryan Moshe</dc:creator>
      <pubDate>Mon, 22 Jul 2019 23:49:18 +0000</pubDate>
      <link>https://dev.to/oryanmoshe/replicating-postgressql-into-memsql-s-columnstore-322h</link>
      <guid>https://dev.to/oryanmoshe/replicating-postgressql-into-memsql-s-columnstore-322h</guid>
      <description>&lt;h2&gt;
  
  
  Making the impossible hard
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6d6niyj52r6x5mijfezt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6d6niyj52r6x5mijfezt.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  &lt;center&gt;&lt;em&gt;I… Don't think this is how it should look like&lt;/em&gt;&lt;/center&gt;
&lt;/h5&gt;

&lt;p&gt;So it's &lt;em&gt;this&lt;/em&gt; time of the year &lt;strong&gt;again&lt;/strong&gt;, we need to upgrade our MemSQL cluster and expand our contract to fit the new cluster topology.&lt;/p&gt;

&lt;p&gt;We really outdid ourselves this time, expanding to a 1TB cluster is impressive, especially when it's completely not justified.&lt;/p&gt;




&lt;h3&gt;
  
  
  The background
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Wait. A 1TB cluster?
&lt;/h4&gt;

&lt;p&gt;Yeah yeah, call us spoiled, but querying on PostgresSQL (PG from now on) is just not the same.&lt;/p&gt;

&lt;p&gt;Sure, you can get &lt;em&gt;ok&lt;/em&gt; speeds if you're using the correct indexes and optimize your queries, but it's not even comparable to the performance you get from the memory based rowstore, or the insanely fast aggregations of the columnstore.&lt;/p&gt;

&lt;h4&gt;
  
  
  A short (short) summary of Mem's different storage types
&lt;/h4&gt;

&lt;p&gt;So we basically have 2 types of storage in Mem, rowstore and columnstore.&lt;/p&gt;

&lt;p&gt;The rowstore is stored pretty much like any other database, but in the memory instead of the disk (crazy fast). This means each row is stored together with all of its columns&lt;/p&gt;

&lt;p&gt;The columnstore is sort of a transposed rowstore, instead of storing rows we store columns (&lt;em&gt;thank you captain obvious&lt;/em&gt;) which allows us to make aggregations stupid fast (think about it, instead of going to each row and summing the "cost" column, we can just go to the "cost" column and sum it up). The columnstore is stored on the disk.&lt;/p&gt;

&lt;p&gt;The issue is MemSQL's license costs more as we have more memory in our cluster, not to mention the cost of the machines themselves (1TB of memory isn't exactly cheap)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"So why not store everything in the columnstore? It's cheaper both license and infrastructure wise, and it's stupid fast!"&lt;/em&gt; You might ask (if you talk to yourself while reading tech articles)&lt;/p&gt;

&lt;p&gt;So here's the catch - the way the data is stored in a columnstore makes it incredibly fast in aggregated queries, and allows amazing compression, but updating a row is &lt;strong&gt;slow&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;How slow? If we need to update some columns for rows in a specific day, it's faster for us to delete the data from this day and re-insert the updated one instead of updating the existing rows.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://i.giphy.com/media/1xkMJIvxeKiDS/giphy.gif"&gt;
&lt;/h2&gt;

&lt;h4&gt;
  
  
  So how do we store our data?
&lt;/h4&gt;

&lt;p&gt;Well, in my team we use 7 flavors of databases (might be more, can't really keep track these days) &lt;em&gt;but&lt;/em&gt; the main ones are PostgresSQL, hosted and managed by RDS (for transactional processing) and MemSQL, hosted on EC2 and managed by yours truly (for analytical processing*)&lt;/p&gt;

&lt;p&gt;Instinctively, most of our data is stored in PG (excluding some large columnstore tables containing North of 8B records)&lt;/p&gt;

&lt;p&gt;The problem is, once you go Mem you never go back, so we created a replication service that can replicate a row from PG to Mem's rowstore in real-time. This allows us to enrich our columnstore only tables, create ETLs, and most importantly, speed up queries.&lt;/p&gt;

&lt;p&gt;If you're here you either use Mem and thus know its performance, or just like to go around dev.to, reading random articles about niche DBs. If you're the latter let me hit you with some numbers.&lt;/p&gt;

&lt;p&gt;A completely reasonable query, consisting of 6 joins, took 30 minutes to run on PG. After optimizing it for 2–3 hours, adding indexes, banging my head against the wall and praying for swift ending, I was able to cut it down to 3 minutes.&lt;/p&gt;

&lt;p&gt; &lt;br&gt;
Taking exactly the original query (the 30 minutes one) and running it on Mem, it took &lt;em&gt;1.87 seconds&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;img src="https://i.giphy.com/media/nC1toglzIfNFC/giphy.gif"&gt;
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The real deal
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Problems definition, AKA what's making me lose sleep
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/D12CsrRNv7gL6/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/D12CsrRNv7gL6/giphy.gif"&gt;&lt;/a&gt;&lt;br&gt;
So Mem is expensive, we're almost at our &lt;strong&gt;new&lt;/strong&gt; license limit (after more than doubling it) and there's no way we can go back to query exclusively on PG.&lt;/p&gt;

&lt;p&gt;The solution seems simple, move big tables to the columnstore, free up some memory so you don't have to increase your license and upgrade your machines.&lt;/p&gt;

&lt;p&gt;For this article I'll use our table &lt;code&gt;touch_points&lt;/code&gt; as an example, it's our largest (both in memory and row count) table stored in a rowstore - it has over 180M rows, and weighs more than 190GB.&lt;/p&gt;

&lt;p&gt;Why is it in our rowstore? First, cause we replicate it from PG, and so far our service only supports replicating to rowstore tables, but, more importantly, it needs to be updated. Out of 30 columns, 2 might get updated - &lt;code&gt;visitor_id&lt;/code&gt; and &lt;code&gt;cost&lt;/code&gt;.&lt;/p&gt;


&lt;h3&gt;
  
  
  Solutions
&lt;/h3&gt;
&lt;h4&gt;
  
  
  The first solution
&lt;/h4&gt;

&lt;p&gt;So this was the "correct" solution design-wise.&lt;/p&gt;

&lt;p&gt;In short, using ActiveRecord callbacks I kept 2 tables up to date, one is the &lt;code&gt;touch_points&lt;/code&gt; table in the columnstore, containing all columns that exist presently on &lt;code&gt;touch_points&lt;/code&gt; &lt;strong&gt;except&lt;/strong&gt; the 2 that get updated. Other than &lt;code&gt;touch_points&lt;/code&gt; I created a table called &lt;code&gt;touch_points_extra_data&lt;/code&gt; in the rowstore, containing the 2 missing columns and 1 ID column that allows me to connect the 2 tables.&lt;/p&gt;

&lt;p&gt;As I said, this was the correct solution design-wise, the problem is that so much could go wrong. With so many moving parts, all dependent on rails hooks, we were sure to get out of sync &lt;strong&gt;sometime&lt;/strong&gt;. Not to mention the fact that we'll have to edit all of our queries from &lt;code&gt;touch_points&lt;/code&gt; to add that extra &lt;code&gt;JOIN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3otPoD3m0h16iRbgNG/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3otPoD3m0h16iRbgNG/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  The second solution, AKA "The bruteforce"
&lt;/h4&gt;

&lt;p&gt;So we realized our top priority is to keep the data correct, and we were willing to make some compromises &lt;em&gt;(foreshadowing)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I decided to replicate the whole table, as is, from PG once in a while. This way we can make sure that (up to the moment of replicating) our data will be identical in both DBs.&lt;/p&gt;

&lt;p&gt;The compromise is that we are used to having this data updated in real time, and now it'll be outdated until the next replication. This is a compromise I'm willing to take.&lt;/p&gt;


&lt;h3&gt;
  
  
  The technical part
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Easier said than done
&lt;/h4&gt;

&lt;p&gt;So apparently replicating a whole table from one DB to another isn't as straightforward as you would think. Especially when the two DBs run on different engines entirely.&lt;/p&gt;

&lt;p&gt;The first thing I tried is using &lt;code&gt;pg_dump&lt;/code&gt;, with the &lt;code&gt;plain&lt;/code&gt; file format (which essentially creates a file with loads of &lt;code&gt;INSERT&lt;/code&gt; statements) and then convert it to mysql syntax and load to Mem.&lt;/p&gt;

&lt;p&gt;Sounds great right? I started the &lt;code&gt;pg_dump&lt;/code&gt;, and 5 hours later it wasn't even close to finishing, while the dump file was already at 60GB. &lt;code&gt;pg_dump&lt;/code&gt; with the &lt;code&gt;plain&lt;/code&gt; option is the most inefficient way to store data. 5 hours delay in replication is unacceptable.&lt;/p&gt;
&lt;h4&gt;
  
  
  If at first you don't succeed.. Fail again
&lt;/h4&gt;

&lt;p&gt;The next thing I tried was using the &lt;code&gt;COPY&lt;/code&gt; command of PG, this command can copy (duh) a table, or a query into a &lt;code&gt;FILE&lt;/code&gt;, a &lt;code&gt;PROGRAM&lt;/code&gt;, or &lt;code&gt;STDOUT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;First I tried using the STDOUT option (the simplest one, and it doesn't create a footprint of a huge dump file)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="nt"&gt;-U&lt;/span&gt; read_user &lt;span class="nt"&gt;-h&lt;/span&gt; very-cool-hostname.rds.amazonaws.com &lt;span class="nt"&gt;-p&lt;/span&gt; 5432 &lt;span class="nt"&gt;-d&lt;/span&gt; very_cool_db &lt;span class="nt"&gt;-c&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\C&lt;/span&gt;&lt;span class="s2"&gt;OPY (SELECT * FROM touch_points) TO STDOUT&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
WITH(DELIMITER ',', FORMAT CSV, NULL 'NULL', QUOTE '&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;');"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; touch_points.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it worked! I got a "dump" file from PG containing our whole &lt;code&gt;touch_points&lt;/code&gt; table, in just under 20 minutes.&lt;/p&gt;

&lt;p&gt;Now we just need to import it to Mem, but why &lt;strong&gt;do&lt;/strong&gt; I need the file? I can just pipe the result right from PG straight into Mem!&lt;/p&gt;

&lt;p&gt;So I needed to create the part where Mem receives this csv-like table and loads it into the db. Luckily Mem is MySQL compatible and provides us with the &lt;code&gt;LOAD DATA&lt;/code&gt; clause!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;LOAD&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="k"&gt;LOCAL&lt;/span&gt; &lt;span class="n"&gt;INFILE&lt;/span&gt; &lt;span class="s1"&gt;'/dev/stdin'&lt;/span&gt;
  &lt;span class="n"&gt;SKIP&lt;/span&gt; &lt;span class="n"&gt;DUPLICATE&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="n"&gt;ERRORS&lt;/span&gt;
  &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;touch_points_columnstore&lt;/span&gt;
  &lt;span class="n"&gt;FIELDS&lt;/span&gt;
    &lt;span class="n"&gt;TERMINATED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="s1"&gt;','&lt;/span&gt;
    &lt;span class="n"&gt;ENCLOSED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="s1"&gt;'"'&lt;/span&gt;
    &lt;span class="n"&gt;ESCAPED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
  &lt;span class="n"&gt;LINES&lt;/span&gt;
    &lt;span class="n"&gt;TERMINATED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
  &lt;span class="n"&gt;MAX_ERRORS&lt;/span&gt; &lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, as I said we want to pipe that data right into Mem, so we need to create a connection to our DB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-h&lt;/span&gt; memsql.very-cool-hostname.com &lt;span class="nt"&gt;-u&lt;/span&gt; write_user &lt;span class="nt"&gt;-P&lt;/span&gt; 3306 &lt;span class="nt"&gt;-D&lt;/span&gt; very_cool_db&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'4m4z1nglyS3cur3P455w0rd'&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;--local-infile&lt;/span&gt; &lt;span class="nt"&gt;--default-auth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mysql_native_password &lt;span class="nt"&gt;-e&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"LOAD DATA LOCAL INFILE '/dev/stdin' SKIP DUPLICATE KEY ERRORS&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
INTO TABLE touch_points_columnstore FIELDS TERMINATED BY ','&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
ENCLOSED BY '&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;' ESCAPED BY '' LINES TERMINATED BY '&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n' MAX_ERRORS 1000000;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then just pipe the data from PG to that connection!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="nt"&gt;-U&lt;/span&gt; read_user &lt;span class="nt"&gt;-h&lt;/span&gt; very-cool-hostname.rds.amazonaws.com &lt;span class="nt"&gt;-p&lt;/span&gt; 5432 &lt;span class="nt"&gt;-d&lt;/span&gt; very_cool_db &lt;span class="nt"&gt;-c&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\C&lt;/span&gt;&lt;span class="s2"&gt;OPY (SELECT * FROM touch_points) TO STDOUT&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
WITH(DELIMITER ',', FORMAT CSV, NULL 'NULL', QUOTE '&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;');"&lt;/span&gt; |&lt;span class="se"&gt;\&lt;/span&gt;
mysql &lt;span class="nt"&gt;-h&lt;/span&gt; memsql.very-cool-hostname.com &lt;span class="nt"&gt;-u&lt;/span&gt; write_user &lt;span class="nt"&gt;-P&lt;/span&gt; 3306 &lt;span class="nt"&gt;-D&lt;/span&gt; very_cool_db&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'4m4z1nglyS3cur3P455w0rd'&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;--local-infile&lt;/span&gt; &lt;span class="nt"&gt;--default-auth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mysql_native_password &lt;span class="nt"&gt;-e&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"LOAD DATA LOCAL INFILE '/dev/stdin' SKIP DUPLICATE KEY ERRORS&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
INTO TABLE touch_points_columnstore FIELDS TERMINATED BY ','&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
ENCLOSED BY '&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;' ESCAPED BY '' LINES TERMINATED BY '&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n' MAX_ERRORS 1000000;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And... It worked! But it took an 2 hours to complete. I'm &lt;em&gt;sure&lt;/em&gt; we can do better than that.&lt;/p&gt;

&lt;h4&gt;
  
  
  Compression is your friend
&lt;/h4&gt;

&lt;p&gt;So two cool things important to understand about loading data into Mem are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When inserting a data file into Mem, it copies the file locally to the aggregator and splits the file between the nodes of the cluster, speeding up the data load significantly.&lt;/li&gt;
&lt;li&gt;Mem supports receiving gzipped-compressed data files.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Combining these two pieces of information made me understand that creating the file in the middle maybe isn't as bad as I thought.&lt;/p&gt;

&lt;p&gt;I can compress that file, making storage a non-issue, it'll also speed up the transfer of the file to the aggregator (before splitting) by cutting out most of the network related latency, and it'll allow Mem to split the data between the nodes.&lt;/p&gt;

&lt;p&gt;Let's do it!&lt;/p&gt;

&lt;p&gt;First of all I need to modify the PG part so instead of piping the content to &lt;code&gt;STDIN&lt;/code&gt;, it pipes it to a &lt;code&gt;PROGRAM&lt;/code&gt;, and in our case, &lt;code&gt;gzip&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="nt"&gt;-U&lt;/span&gt; read_user &lt;span class="nt"&gt;-h&lt;/span&gt; very-cool-hostname.rds.amazonaws.com &lt;span class="nt"&gt;-p&lt;/span&gt; 5432 &lt;span class="nt"&gt;-d&lt;/span&gt; very_cool_db &lt;span class="nt"&gt;-c&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\C&lt;/span&gt;&lt;span class="s2"&gt;OPY (SELECT * FROM touch_points) TO PROGRAM 'gzip &amp;gt; /data/tmp/replication/touch_points_columnstore.gz'&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
WITH(DELIMITER ',', FORMAT CSV, NULL 'NULL', QUOTE '&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;');"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After we created this tmp file we need to load it. Luckily the only thing we have to do is to change the source of the input file!&lt;br&gt;
Our finished script looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="nt"&gt;-U&lt;/span&gt; read_user &lt;span class="nt"&gt;-h&lt;/span&gt; very-cool-hostname.rds.amazonaws.com &lt;span class="nt"&gt;-p&lt;/span&gt; 5432 &lt;span class="nt"&gt;-d&lt;/span&gt; very_cool_db &lt;span class="nt"&gt;-c&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\C&lt;/span&gt;&lt;span class="s2"&gt;OPY (SELECT * FROM touch_points) TO PROGRAM 'gzip &amp;gt; /data/tmp/replication/touch_points_columnstore.gz'&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
WITH(DELIMITER ',', FORMAT CSV, NULL 'NULL', QUOTE '&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;');"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
mysql &lt;span class="nt"&gt;-h&lt;/span&gt; memsql.very-cool-hostname.com &lt;span class="nt"&gt;-u&lt;/span&gt; write_user &lt;span class="nt"&gt;-P&lt;/span&gt; 3306 &lt;span class="nt"&gt;-D&lt;/span&gt; very_cool_db&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="s1"&gt;'4m4z1nglyS3cur3P455w0rd'&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;--local-infile&lt;/span&gt; &lt;span class="nt"&gt;--default-auth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mysql_native_password &lt;span class="nt"&gt;-e&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="s2"&gt;"LOAD DATA LOCAL INFILE '/data/tmp/replication/touch_points_columnstore.gz' SKIP DUPLICATE KEY ERRORS&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
INTO TABLE touch_points_columnstore FIELDS TERMINATED BY ','&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
ENCLOSED BY '&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;' ESCAPED BY '' LINES TERMINATED BY '&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n' MAX_ERRORS 1000000;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it!&lt;/p&gt;

&lt;p&gt;The created file weighs 7GB, and the whole process takes less than 20 minutes, so we can run it once an hour and have semi-realtime data!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/a0h7sAqON67nO/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/a0h7sAqON67nO/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Obviously this wasn't the end, I wrapped it up in a nice rails module that allows me to replicate any query from PG to Mem easily, including truncating the old data and using 2 tables to minimize the downtime during replication.&lt;/p&gt;

&lt;p&gt;Feel free to contact me with any questions!&lt;br&gt;
 &lt;br&gt;
 &lt;br&gt;
&lt;a&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;*including but not limited to analytics and transactions.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>memsql</category>
      <category>sql</category>
      <category>postgressql</category>
    </item>
    <item>
      <title>I spend one hour a week optimizing my development environment.</title>
      <dc:creator>Oryan Moshe</dc:creator>
      <pubDate>Sun, 14 Jul 2019 23:57:07 +0000</pubDate>
      <link>https://dev.to/oryanmoshe/i-spend-one-hour-a-week-optimizing-my-development-environment-l9a</link>
      <guid>https://dev.to/oryanmoshe/i-spend-one-hour-a-week-optimizing-my-development-environment-l9a</guid>
      <description>&lt;h2&gt;
  
  
  The story of my unreasonable efficiency obsession - and the tools I use to fuel it
&lt;/h2&gt;

&lt;p&gt;If you're a productivity freak like me you probably found yourself in the following situation (presumably in the last week):&lt;/p&gt;

&lt;p&gt;You have a task to complete, you sit down, open up your laptop and suddenly, it happens.&lt;br&gt;
That annoying little thing you have to do &lt;em&gt;every single time.&lt;/em&gt; It might happen once an hour, once a day, maybe once a week, but it drives you bananas. &lt;/p&gt;

&lt;p&gt;It could be something small like inserting your MFA codes, closing a pop-up or even switching to "normal" mode on vim.&lt;br&gt;
It could be something more time consuming like rearranging your displays layout after replugging your Mac, changing your connection string to a readonly production database, or restarting your environment when changes are not affecting it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It doesn't matter&lt;/strong&gt;, the point is it's something you're doing instead of what you'd rather be doing.&lt;/p&gt;

&lt;p&gt;8 months ago I decided to stop the madness. I started reserving 1 hour a week to get rid of these little nuisances. &lt;/p&gt;


&lt;h2&gt;
  
  
  Let's talk tools
&lt;/h2&gt;

&lt;p&gt;So there are many tools that help me work towards my goal of ultimate efficiency. Some of them are free, some of them cost some bucks, heck, I even paid 50 bucks for one of them. You don't have to use the ones I suggest, and for every paid one I'm sure you can find a free one or create one yourself.&lt;/p&gt;
&lt;h3&gt;
  
  
  BetterTouchTool &amp;gt; &lt;em&gt;$7.5 - $21&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://folivora.ai/"&gt;BetterTouchTool&lt;/a&gt; is the first tool I used to customize my development experience, I installed it even before starting my current position (I received my laptop beforehand) and I can't believe how much I use it every day.&lt;br&gt;
In a nutshell - it allows you to customize everything "input related" in your Mac, including your Magic Mouse, trackpad, keyboard and even your Touch Bar (if you use a compatible version)&lt;/p&gt;
&lt;h3&gt;
  
  
  Karabiner-Elements &amp;gt; &lt;em&gt;Free - Open source&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://pqrs.org/osx/karabiner/"&gt;Karabiner&lt;/a&gt; is one of those tools I expect every OS to have built in. It allows you to remap any key on your keyboard to do anything you want. Why isn't it a standard??&lt;/p&gt;
&lt;h3&gt;
  
  
  Alfred + Powerpack &amp;gt; &lt;em&gt;£23 - £39&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.alfredapp.com/"&gt;Alfred&lt;/a&gt; is probably the first tool I'll ever go to when trying to solve a workflow issue. Alfred (a free tool, massively extended by a paid "Powerpack") has way too many functions to tell you about here, but it's essentially Spotlight on steroids. It allows countless customizations and features out-of-the-box improvements that'll make you wish you met it before.&lt;/p&gt;


&lt;h2&gt;
  
  
  The changes that changed my life
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Customizing my Touch Bar
&lt;/h3&gt;

&lt;p&gt;If you have a Macbook pro with a Touch Bar, sooner or later you probably thought to yourself: &lt;em&gt;"Damn, that is one piece of unutilized potentiel"&lt;/em&gt;.&lt;br&gt;
And it is, Apple definitely dropped the ball on that front.&lt;br&gt;
Luckily, I used BetterTouchTool to customize it and make it not suck!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhs34ysmssqfotasba713.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhs34ysmssqfotasba713.png" width="800" height="22"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fze8oxoh3quw9nh9lk272.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fze8oxoh3quw9nh9lk272.png" width="800" height="22"&gt;&lt;/a&gt;&lt;br&gt;
As you can see I left the &lt;code&gt;esc&lt;/code&gt; key in the top left corner (we'll talk about the &lt;code&gt;esc&lt;/code&gt; situation later), and I put a few shortcuts to applications I frequently use (Finder, Hyper terminal, VS Code)&lt;/p&gt;

&lt;p&gt;That red &lt;code&gt;Production&lt;/code&gt; / green &lt;code&gt;Development&lt;/code&gt; block indicates what is the current state of my &lt;code&gt;database.yml&lt;/code&gt; file (in rails based applications your connection strings are kept there), this allows me to know whether I'm working against my local DB or readonly production DB in a glance.&lt;/p&gt;

&lt;p&gt;I also added a block that shows me my current internal IP address (useful when I want to share a link to a local server with a colleague), a mute button, a DateTime block with seconds (important when wanting to get a feel about query performance) that also takes me to my calendar on tap, and a "Coffee Break" button that locks my laptop.&lt;/p&gt;

&lt;p&gt;Obviously when Spotify or Youtube are playing I have a block appear in the middle with some useful functions.&lt;/p&gt;

&lt;p&gt;Another cool feature I utilized is 2 fingers drag to control brightness, and 3 fingers drag to control volume.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Replacing my Caps Lock with &lt;code&gt;ESC&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;If you know me, you probably know I have a mild case of keyboard fetish. I build them, I collect them, I try to make the ones that best suit my style.&lt;/p&gt;

&lt;p&gt;One thing I have to do on every system I use is remap my Caps Lock key to &lt;code&gt;ESC&lt;/code&gt;, and especially in this specific Mac. I mean, &lt;strong&gt;who on earth would prefer § over a physical &lt;code&gt;esc&lt;/code&gt; key??&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you use vim you know how important it is to have the &lt;code&gt;esc&lt;/code&gt; key as close to you as possible, and as I started using it more and more I realized I'm using it for things other than vim. Closing pop-ups, blurring inputs, opening the console in Chrome devtools. Escape is the unsung hero of keys.&lt;/p&gt;

&lt;p&gt;I use Karabiner-Elements to remap the useless Caps Lock (it's 2019, if you use Caps Lock it's time for some re-education) into an &lt;code&gt;esc&lt;/code&gt;. Can't get easier than that.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Space Cadet Shift
&lt;/h3&gt;

&lt;p&gt;In case you haven't read &lt;a href="http://stevelosh.com/blog/2012/10/a-modern-space-cadet/#shift-parentheses"&gt;this&lt;/a&gt; excellent article by Steve Losh, I highly recommend you do, but the customization I liked (and used) the most ever since I read it is the "Space Cadet Shift Parentheses".&lt;/p&gt;

&lt;p&gt;It turns your &lt;code&gt;left_shift&lt;/code&gt; key into a &lt;code&gt;(&lt;/code&gt; and your &lt;code&gt;right_shift&lt;/code&gt; key into a &lt;code&gt;)&lt;/code&gt; if they're pressed briefly, and treats them as normal shift modifier keys when held and combined with another key.&lt;/p&gt;

&lt;p&gt;This allows me to type parentheses with one keystroke, which makes much more sense than the traditional way of &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;9&lt;/code&gt;, as I probably use parentheses hundreds of times a day.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Clipboard history
&lt;/h3&gt;

&lt;p&gt;This is truly a game changer. I specifically use Alfred's clipboard from the Powerpack binded to a &lt;code&gt;cmd&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;v&lt;/code&gt; shortcut, but you can use the free software "Jumpcut" instead.&lt;br&gt;
What this essentially does is keep a history of your copied items, including text &lt;em&gt;and&lt;/em&gt; images.&lt;/p&gt;

&lt;p&gt;Originally, I started using it to stop these &lt;em&gt;"F*ck, I copied over that super important thing I had in my clipboard and now I have to go back and copy it again"&lt;/em&gt; moments I kept having, but as time went on I started using it as an actual tool.&lt;/p&gt;

&lt;p&gt;I copy multiple items from one application, then go to another application and paste them as needed without having to switch back.&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Rearranging the display layout
&lt;/h3&gt;

&lt;p&gt;If you have a Mac and 2 displays you're probably familiar with the issue.&lt;br&gt;
You disconnect your laptop to go to a meeting, and when you come back your screens are all messed up, the spaces go to wherever they feel like and you're left there dragging windows from one display to the other for 2 minutes.&lt;/p&gt;

&lt;p&gt;What I discovered is that you can change your main display 3 times (if you do it in the correct order) and the displays will go back to where you want them!&lt;br&gt;
This in itself is a huge time-saver, but I wanted to take it to the next level, so I created an Alfred workflow.&lt;/p&gt;

&lt;p&gt;A workflow is a set of actions, these can be scripts, changes in settings, push notifications and a ton of other actions, executed in order and triggered by a keyword in Alfred or a keyboard shortcut.&lt;/p&gt;

&lt;p&gt;Using the &lt;a href="https://github.com/jakehilborn/displayplacer"&gt;free cli tool&lt;/a&gt; &lt;code&gt;displayplacer&lt;/code&gt;, I was able to record the current displays state after each "main display change", and after I had all 3 I chained them together in a workflow triggered by the keyword arr (short for arrange).&lt;/p&gt;

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

&lt;p&gt;Now every time I sit at my desk, I plug in my displays, press &lt;code&gt;⌥&lt;/code&gt; + &lt;code&gt;space&lt;/code&gt; (my Alfred shortcut), type &lt;code&gt;arr&lt;/code&gt; in, and the displays are fixed!&lt;/p&gt;
&lt;h3&gt;
  
  
  6. Changing my DB connection string
&lt;/h3&gt;

&lt;p&gt;The amounts of data we work with don't allow us to pull the data to our local machine and work on it, so we sometimes have to change our connection string (our &lt;code&gt;database.yml&lt;/code&gt; file) to connect to a read-only replica of our production DB to see how the data looks on a new dashboard or what not.&lt;/p&gt;

&lt;p&gt;This process happens about 6-7 times a day, and every time I have to open the &lt;code&gt;database.yml&lt;/code&gt; in my editor, uncomment / comment the lines of the production DB, save the file and restart my &lt;code&gt;rails server&lt;/code&gt; (which we'll solve later)&lt;/p&gt;

&lt;p&gt;I created an Alfred workflow that simply replaces my &lt;code&gt;database.yml&lt;/code&gt; with the correct one according to the command I typed in, &lt;strong&gt;and&lt;/strong&gt; saves a file stating which file is currently in use (remember that red/green block in the Touch Bar?)&lt;/p&gt;

&lt;p&gt;Typing &lt;code&gt;dbd&lt;/code&gt; changes my DB to &lt;strong&gt;d&lt;/strong&gt;evelopment, and &lt;code&gt;dbp&lt;/code&gt; to &lt;strong&gt;p&lt;/strong&gt;roduction.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1nqdalo27e8z41tcb5p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1nqdalo27e8z41tcb5p.png" width="800" height="189"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  7. Automatically restarting a local server
&lt;/h3&gt;

&lt;p&gt;Anyone who developed some node.js knows &lt;code&gt;nodemon&lt;/code&gt;, a great utility that allows you to run a node server, and restarts it whenever the files change.&lt;br&gt;
But did you know &lt;code&gt;nodemon&lt;/code&gt; can work on any cli tool and watch any file type?&lt;/p&gt;

&lt;p&gt;Changing my &lt;code&gt;rs&lt;/code&gt; alias (&lt;code&gt;rails server&lt;/code&gt;) to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;rs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"nodemon --watch config -e rb,yml --exec &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;rails server -b 0.0.0.0 -p 3002&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Allows me to run my server, and whenever a file in the &lt;code&gt;config&lt;/code&gt; directory with an &lt;code&gt;rb&lt;/code&gt; or &lt;code&gt;yml&lt;/code&gt; extension is changed, it restarts the server! No more manual restart on &lt;code&gt;database.yml&lt;/code&gt; change!&lt;/p&gt;

&lt;h3&gt;
  
  
  8. &lt;code&gt;sudo&lt;/code&gt; TouchID
&lt;/h3&gt;

&lt;p&gt;Whenever I need to type the master password of my Mac I'm annoyed. Why? Because I have TouchID! Why should I type a password instead of biometrically identifying myself?&lt;/p&gt;

&lt;p&gt;I discovered that I can add a simple line to the &lt;code&gt;sudo&lt;/code&gt; PAM (Pluggable Authentication Module) and from now on every time I need to &lt;code&gt;sudo&lt;/code&gt; a command I just have to put my finger on the sensor!&lt;br&gt;
Just add the following line to the top of your &lt;code&gt;/private/etc/pam.d/sudo&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auth sufficient pam_tid.so
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9. MFA without my phone
&lt;/h3&gt;

&lt;p&gt;For this one I'll refer you to &lt;a href="https://github.com/moul/alfred-workflow-gauth"&gt;this&lt;/a&gt; fantastic workflow that inspired me, but in short - you can create an Alfred workflow that pastes an MFA code without you having to open your phone.&lt;/p&gt;

&lt;p&gt;To keep my accounts secure (there's a reason for MFA after all) I changed the script permissions to "root only" and ran it as &lt;code&gt;sudo&lt;/code&gt;.&lt;br&gt;
If you remember from #8, sudoing on my Mac requires a fingerprint, and this allows me to protect my MFA tokens with a fingerprint, while not having to reach for my phone every time GitHub invalidates my session.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. Starting my development environment
&lt;/h3&gt;

&lt;p&gt;Our development environment is semi-complex, and personally I like to work in a specific way.&lt;br&gt;
My terminal is Hyper, customized exactly as I like it, I have one tab split into 3 sections, one for &lt;code&gt;docker-compose&lt;/code&gt;, one for our &lt;code&gt;npm&lt;/code&gt; transpiler, and one for the &lt;code&gt;rails server&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Other than that tab I have 2 more tabs, one for a &lt;code&gt;rails console&lt;/code&gt; and the other for &lt;code&gt;git&lt;/code&gt; management.&lt;/p&gt;

&lt;p&gt;Instead of opening all of these tabs and typing all of these commands every time I'm starting my devenv, I created an Alfred workflow triggered by the keyboard shortcut &lt;code&gt;cmd&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;b&lt;/code&gt; that does all of that for me!&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmterctpj06tn86l0il69.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmterctpj06tn86l0il69.png" width="800" height="728"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Honorable mentions
&lt;/h2&gt;

&lt;p&gt;Not all of my optimizations have a story behind them, some are trivial and are still huge time savers&lt;/p&gt;

&lt;h3&gt;
  
  
  Aliases
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use shell aliases.&lt;/strong&gt;&lt;br&gt;
Whenever you have a long command you have to type more than once in a lifetime, throw it into an alias. You'll be surprised by how much you use it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Terminal configuration
&lt;/h3&gt;

&lt;p&gt;Apart from aliases there are a lot of customizations to do in your terminal. From shell customization including &lt;code&gt;fzf&lt;/code&gt; and history auto-complete to better tab management and better remoting tools (like &lt;code&gt;mosh&lt;/code&gt;)&lt;br&gt;
The terminal is your friend and you spend a lot of time in it. Treat it nicely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Calculator
&lt;/h3&gt;

&lt;p&gt;You can use Spotlight / Alfred as a calculator. Apparently this isn't as known as I thought it was.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffq82ev53ke2j4yi5v8ml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffq82ev53ke2j4yi5v8ml.png" width="800" height="138"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using spaces
&lt;/h3&gt;

&lt;p&gt;I know it's hard to get used to compared to Windows windows, but honestly spaces are the best way to get around a Mac. Take the time, learn to use them and learn all the gestures. Navigating spaces is &lt;strong&gt;way&lt;/strong&gt; faster than dragging windows around.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trackpad 3 fingers drag
&lt;/h3&gt;

&lt;p&gt;You can drag with 3 fingers instead of click and dragging on the trackpad. Turn on this gesture, your fingers will thank you!&lt;br&gt;
You can find it in Preferences &amp;gt; Accessibility &amp;gt; Mouse &amp;amp; Trackpad &amp;gt; Trackpad Options... &amp;gt; Enable dragging &amp;gt; three finger drag.&lt;/p&gt;




&lt;p&gt;I have many more optimizations just like the ones posted here, if you'd like me to create a follow-up post just say 🙃&lt;/p&gt;

&lt;p&gt;You spend hours in front of your computer, make sure every minute counts.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>career</category>
      <category>tools</category>
      <category>mac</category>
    </item>
    <item>
      <title>The object that misbehaved — window.performance.memory</title>
      <dc:creator>Oryan Moshe</dc:creator>
      <pubDate>Thu, 11 Jul 2019 23:05:16 +0000</pubDate>
      <link>https://dev.to/oryanmoshe/the-object-that-misbehaved-window-performance-memory-5hag</link>
      <guid>https://dev.to/oryanmoshe/the-object-that-misbehaved-window-performance-memory-5hag</guid>
      <description>&lt;h1&gt;
  
  
  How I troubleshoot issues that don’t make sense
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fo5knc5kj2ovoxce7eaia.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fo5knc5kj2ovoxce7eaia.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Today we wanted to start tracking some new performance metrics on our landing pages, and more specifically, the memory usage.&lt;br&gt;
As usual, we got the data from our trusty &lt;code&gt;window.performance&lt;/code&gt; object (from now on will be referred to as &lt;em&gt;"the enemy"&lt;/em&gt;), stringified it before sending and…&lt;/p&gt;

&lt;p&gt;Surprisingly, all we got was &lt;code&gt;"{}"&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;How can it be? We have an object, with properties we can access, but stringifying it returns an empty object!&lt;/p&gt;


&lt;h3&gt;
  
  
  Down the rabbit hole I go
&lt;/h3&gt;

&lt;p&gt;I started digging deeper into it, with the goal of turning &lt;em&gt;the enemy&lt;/em&gt; into a string without hard coding the property names.&lt;/p&gt;
&lt;h5&gt;
  
  
  I tried spreading it
&lt;/h5&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// {}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h5&gt;
  
  
  I tried getting its components
&lt;/h5&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// []&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// []&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h5&gt;
  
  
  I tried destructuring it
&lt;/h5&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;jsHeapSizeLimit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jsHeapSizeLimit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 2330000000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And surprisingly — it worked!&lt;/p&gt;

&lt;p&gt;With great confidence that I’m going to crack this I tried "rest" destructuring:&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// {}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is going on? Why does this specific object refuse to play nice?&lt;/p&gt;




&lt;h3&gt;
  
  
  A light at the end of the tunnel (that turns out to be a train)
&lt;/h3&gt;

&lt;p&gt;After some more tinkering I found another thing that didn’t make sense.&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="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// totalJSHeapSize: 13400000&lt;/span&gt;
  &lt;span class="c1"&gt;// usedJSHeapSize: 12700000&lt;/span&gt;
  &lt;span class="c1"&gt;// jsHeapSizeLimit: 2330000000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Iterating over the enemy’s properties does work, but getting its properties names doesn’t? Even Object.getOwnPropertyNames failed!&lt;/p&gt;

&lt;p&gt;Although this solution could work for me (remember, my original goal was "turning &lt;em&gt;the enemy&lt;/em&gt; into a string without hard coding the property names") I wanted to find a more elegant solution, and wanted to get to the bottom of this.&lt;/p&gt;




&lt;h3&gt;
  
  
  The first solution, AKA "not good enough"
&lt;/h3&gt;

&lt;p&gt;The next part of my trip was when I started playing around with the prototype of &lt;em&gt;the enemy&lt;/em&gt;, called &lt;code&gt;MemoryInfo&lt;/code&gt;. I tried changing the prototype, assigning &lt;em&gt;the enemy&lt;/em&gt; to a new object with a different prototype, creating an array from the enemy, and eventually playing around with a combination of all the techniques mentioned above.&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="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOwnPropertyNames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// []&lt;/span&gt;
  &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOwnPropertyNames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__proto__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// &lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;totalJSHeapSize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;usedJSHeapSize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jsHeapSizeLimit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;constructor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Success! Well… Sort of. I got the prototype property names, and I could use this array to get what I want using the following snippet&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOwnPropertyNames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__proto__&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constructor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// " {"totalJSHeapSize":20500000,"usedJSHeapSize":18200000,"jsHeapSizeLimit":2330000000}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h6&gt;
  
  
  &lt;center&gt;&lt;em&gt;Not great, not terrible.&lt;/em&gt;&lt;/center&gt;
&lt;/h6&gt;

&lt;p&gt;And it does work (and it is a one-liner like I love), don’t get me wrong, but it’s just not as elegant as I wanted it to be, and nothing made any sense anymore.&lt;/p&gt;




&lt;h3&gt;
  
  
  I am one with the code
&lt;/h3&gt;

&lt;p&gt;Fast forward 1 hour and I discovered that objects have properties, and these properties have metadata (called descriptor) and this metadata defines whether we can enumerate over the property, change it, and how we get it from the object.&lt;/p&gt;

&lt;p&gt;So it must be that the properties have some sort of a metadata tag that prevents getting them in the conventional ways. From MDN I figured that &lt;code&gt;enumerable&lt;/code&gt; is the property that interests me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;true&lt;/code&gt; if and only if this property shows up during enumeration of the properties on the corresponding object.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOwnPropertyDescriptors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__proto__&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// {..., jsHeapSizeLimit: {get: ƒ, set: undefined, enumerable: true, configurable: true}, ...}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the properties do have the &lt;code&gt;enumerable&lt;/code&gt; metadata-property turned on, why aren’t they showing up when using &lt;code&gt;Object.keys&lt;/code&gt; or &lt;code&gt;Object.values&lt;/code&gt;?&lt;/p&gt;




&lt;h3&gt;
  
  
  The not-so-grand finale
&lt;/h3&gt;

&lt;p&gt;Finally, I succeeded in "turning on" the enumerable flags on &lt;em&gt;the enemy&lt;/em&gt;, and my data stringified beautifly&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;finale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOwnPropertyDescriptors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__proto__&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;finale&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "{"totalJSHeapSize":29400000,"usedJSHeapSize":23100000,"jsHeapSizeLimit":2330000000}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h6&gt;
  
  
  &lt;center&gt;&lt;em&gt;Sweet, sweet one-liner&lt;/em&gt;&lt;/center&gt;
&lt;/h6&gt;

&lt;p&gt;So what’s going on here?&lt;/p&gt;

&lt;p&gt;We already saw that the descriptors of the &lt;code&gt;MemoryInfo&lt;/code&gt; prototype should be fine, so the only explanation is that somehow they are not set (or overridden) on &lt;em&gt;the enemy&lt;/em&gt; itself.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;Object.defineProperties&lt;/code&gt; I am able to get a copy of &lt;em&gt;the enemy&lt;/em&gt;, but with the correct descriptors (which we can just get from the prototype using &lt;code&gt;Object.getOwnPropertyDescriptors&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Then just stringify and ship!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fzbc1zinz2o233pfvpaou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fzbc1zinz2o233pfvpaou.png"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>research</category>
      <category>troubleshooting</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
