<?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: Tarun Batra</title>
    <description>The latest articles on DEV Community by Tarun Batra (@tbking).</description>
    <link>https://dev.to/tbking</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%2F165333%2Fc473e514-8ef5-43f4-8ddd-f2f29e0a2f21.png</url>
      <title>DEV Community: Tarun Batra</title>
      <link>https://dev.to/tbking</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tbking"/>
    <language>en</language>
    <item>
      <title>macbook.init(): a dev machine set-up guide</title>
      <dc:creator>Tarun Batra</dc:creator>
      <pubDate>Mon, 03 Oct 2022 05:11:48 +0000</pubDate>
      <link>https://dev.to/tbking/macbookinit-a-dev-machine-set-up-guide-4fmg</link>
      <guid>https://dev.to/tbking/macbookinit-a-dev-machine-set-up-guide-4fmg</guid>
      <description>&lt;p&gt;I started a new job, and I am excited. I get a new MacBook delivered and after keeping the delicate wrapping paper intact during the intricate unboxing, it dawns upon me that it is not &lt;em&gt;my&lt;/em&gt; MacBook. It is a brand-new system devoid of all the custom tools that make my (work) life easier. On top of it, I do not even remember what those tools were, to begin with. Was it &lt;code&gt;oh-my-zsh&lt;/code&gt;, or was I using &lt;code&gt;fish&lt;/code&gt;? What did I do last time to get the top bar show Bluetooth? I end up setting small things like these, slowly every day, until it becomes all familiar, and I forget about them.&lt;/p&gt;

&lt;p&gt;Not this time, I said to myself. As I start working at Okta, I decided to log the steps to set up my system, mostly for future me, but also for anyone else stumbling on the same StackOverflow articles over and over again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update OS
&lt;/h2&gt;

&lt;p&gt;Yeah, just update the OS now that you have patience. It is probably outdated and the next step &lt;em&gt;may&lt;/em&gt; require it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Xcode
&lt;/h2&gt;

&lt;p&gt;Do not wait on some CLI tool to fail on you later and install Xcode now. Might get lucky running &lt;code&gt;xcode-select --install&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Firefox/Brave
&lt;/h2&gt;

&lt;p&gt;Use Safari to install Firefox and Brave and log into them. Also set up extensions like -&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Password managers&lt;/li&gt;
&lt;li&gt;Ad blockers&lt;/li&gt;
&lt;li&gt;Web3 wallets&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Set up terminal environment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install iTerm2
&lt;/h3&gt;

&lt;p&gt;Download the latest build &lt;a href="https://iterm2.com/downloads/stable/latest"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Install homebrew
&lt;/h3&gt;

&lt;p&gt;Refer &lt;a href="https://brew.sh/#install"&gt;https://brew.sh/#install&lt;/a&gt;, or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/bin/bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install oh my zsh
&lt;/h3&gt;

&lt;p&gt;Refer &lt;a href="https://ohmyz.sh/#install"&gt;https://ohmyz.sh/#install&lt;/a&gt;, or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Disable history sharing across zsh sessions
&lt;/h3&gt;

&lt;p&gt;echo "unsetopt share_history\nsetopt no_share_history" &amp;gt;&amp;gt; ~/.zshrc&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup keys 🔑
&lt;/h2&gt;

&lt;p&gt;SSH and GPG keys need to be generated/imported and updated &lt;strong&gt;everywhere&lt;/strong&gt;, especially GitHub.&lt;/p&gt;

&lt;h3&gt;
  
  
  SSH
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ssh-keygen -t ecdsa -b 521&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  PGP
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;gpg --import &amp;lt;private-key-file&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Install nvm
&lt;/h3&gt;

&lt;p&gt;Refer &lt;a href="https://github.com/nvm-sh/nvm#installing-and-updating"&gt;https://github.com/nvm-sh/nvm#installing-and-updating&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-o-&lt;/span&gt; https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install tmuxinator
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew insatll tmuxinator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Customize vim
&lt;/h3&gt;

&lt;p&gt;Restore dotfiles and customize vim and other things.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;At some point, I should figure out that something is wrong with the trackpad and check that box that says &lt;strong&gt;Tap to click&lt;/strong&gt; in settings. This is a living document and I will keep adding things to it, as I find. Now I should get back to that new system I just configured and get some work done!&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>tooling</category>
    </item>
    <item>
      <title>PyPi packages decoded for npm developers</title>
      <dc:creator>Tarun Batra</dc:creator>
      <pubDate>Mon, 31 Aug 2020 20:59:27 +0000</pubDate>
      <link>https://dev.to/tbking/pypi-packages-decoded-for-npm-developers-51j4</link>
      <guid>https://dev.to/tbking/pypi-packages-decoded-for-npm-developers-51j4</guid>
      <description>&lt;p&gt;If you are a developer in Javascript / Node.js ecosystem, you have used &lt;code&gt;npm&lt;/code&gt;. It's the world's largest software registry and the ecosystem around it is advanced and mature. I realized it when I tried to port &lt;code&gt;password-validator&lt;/code&gt;, an open source npm package I maintain, to Python. I spent weeks to figure out things which were very intuitive to me in the Javascript world. This blog will list down some of the key differences I found between the two ecosystems for anybody else who treads the same path. By the end of this post, we should be able to install our package using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &amp;lt;my-awesome-package&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What is a package?
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;npm&lt;/code&gt;, any folder which has a &lt;code&gt;package.json&lt;/code&gt; file is a package. In Python world, we need a file called &lt;code&gt;setup.py&lt;/code&gt; in the root folder to make our project a package for &lt;a href="https://pypi.org/"&gt;PyPi&lt;/a&gt;, a common repository for Python packages. This file is used to declare meta data like name, version, author, and even dependencies (with a catch). Look at this &lt;a href="https://github.com/pypa/sampleproject/blob/master/setup.py"&gt;sample setup.py file&lt;/a&gt; to get an idea.&lt;/p&gt;

&lt;p&gt;Normally, the actual package code goes inside a directory with the package name and it is required to have a &lt;code&gt;__init__.py&lt;/code&gt; file in it to indicate that it is a module. So the directory structure would look more or less like:&lt;/p&gt;

&lt;pre&gt;
package-project
├── setup.py
└── my-package
    ├── __init__.py
    └── lib.py
&lt;/pre&gt;

&lt;p&gt;In this structure your actual package code will go in &lt;code&gt;__init__.py&lt;/code&gt; and/or any other python file in this directory like &lt;code&gt;lib.py&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency management
&lt;/h2&gt;

&lt;p&gt;Dependency management is one area where I found Python ecosystem lacking and primordial. &lt;code&gt;npm&lt;/code&gt; has concepts like &lt;code&gt;dependencies&lt;/code&gt; and &lt;code&gt;devDependencies&lt;/code&gt; and &lt;code&gt;package-lock&lt;/code&gt; which make dependency management easier. The manifest file &lt;code&gt;setup.py&lt;/code&gt; has a section called &lt;code&gt;install-requires&lt;/code&gt; which is a list of the dependencies required to run the project. These dependencies are downloaded while installing the package using &lt;code&gt;pip&lt;/code&gt;. It's not very widely used to define the exact dependencies and dependencies of dependencies.&lt;/p&gt;

&lt;p&gt;Instead, a combination of a virtual environment and requirements file is used.&lt;/p&gt;

&lt;h3&gt;
  
  
  Virtual environment
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;npm&lt;/code&gt; allows installing packages globally or local to a project. In &lt;code&gt;pip&lt;/code&gt;, all packages are installed globally.  To separate the local dependencies from system-wide packages, a virtual environment is created which contains all the dependencies local to the project and even a local Python distribution. Code running in a virtual environment doesn't require any system dependency, so one can be assured that the code should run in all systems wherever the local virtual environment is set up correctly.&lt;/p&gt;

&lt;p&gt;There are multiple ways of setting up a virtual environment, like &lt;code&gt;venv&lt;/code&gt; and &lt;code&gt;virtualenv&lt;/code&gt;. I found former to be the easiest to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv &amp;lt;&lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command creates a virtual environment in directory &lt;code&gt;&amp;lt;dir&amp;gt;&lt;/code&gt; which means this directory will house everything required to run the program including Python and pip distributions. This directory needs not be committed to version control. To use the virtual environment, it has to be "activated".&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;source&lt;/span&gt; &amp;lt;&lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Requirements file
&lt;/h3&gt;

&lt;p&gt;Every virtual environment has its own pip and Python distribution. So it's a clean slate when you start. When we install dependencies, we can make a log of them using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip freeze &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will log all the packages and their versions installed by pip in the file &lt;code&gt;requirements.txt&lt;/code&gt;. The file name can be anything but it's a convention in the Python community to use this name. There is no concept of devDependencies in Python but it's not uncommon for some projects to have a &lt;code&gt;requirements-dev.txt&lt;/code&gt; to log devDependencies of the project. Here's a &lt;a href="https://github.com/tarunbatra/password-validator-python/blob/master/requirements-dev.txt"&gt;sample requirements file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Installing a project's dependencies now can be done with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;code&gt;JSDocs&lt;/code&gt; is the most common way to document code in JavaScript and the list of documentation tools alomst stops there. Documentation in Python is taken very seriously.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sphinx
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.sphinx-doc.org/en/master/"&gt;&lt;code&gt;Sphinx&lt;/code&gt;&lt;/a&gt; is a documentation generator for Python which can be used in various ways thanks to a variety of extensions available. To get started with sphinx, we need a &lt;code&gt;conf.py&lt;/code&gt; file which will have configuration for sphinx. The file can be generated using &lt;code&gt;sphinx-quickstart&lt;/code&gt; CLI utility. Here's a &lt;a href="https://github.com/tarunbatra/password-validator-python/blob/master/docs/source/conf.py"&gt;sample conf.py file&lt;/a&gt; to get started.&lt;/p&gt;

&lt;p&gt;Sphinx and Python ecosystem prefers using &lt;code&gt;reStructuredText&lt;/code&gt; (reST) for documentation instead of Markdown which is common in the JavaScript world. M↓ can be used with Sphinx too, but it's a bit of work and reST is very similar to M↓. Here's a sample &lt;a href="https://raw.githubusercontent.com/tarunbatra/password-validator-python/master/README.rst"&gt;README in reST&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docstrings
&lt;/h2&gt;

&lt;p&gt;Sphinx can look through special comments in Python code and include them in the documentation. These comments which are called &lt;code&gt;Docstrings&lt;/code&gt; are used to describe functions, classes etc. and are very similar to JSDocs in JavaScript and JavaDocs in Java. Here's a method documented with Docstrings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&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;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="s"&gt;''' Performs addition of 2 numbers

  Example:
    &amp;gt;&amp;gt;&amp;gt; res = add(1, 2)
    3
  Returns:
    int: Result of addition
  '''&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a number of formats for writing Docstrings, which can be a topic discussion in itself. I found myself going back to &lt;a href="https://realpython.com/documenting-python-code/#documenting-your-python-code-base-using-docstrings"&gt;this guide by RealPython&lt;/a&gt; and I recommend giving it a read for a deeper understanding of Docstrings. Using the combination of reSt files and Docstrings, we can generate documentation using Sphinx in a form like PDF and HTML. Here's a &lt;a href="https://tarunbatra.com/password-validator-python/api_reference.html"&gt;sample HTML documentation&lt;/a&gt; generated using Sphinx.&lt;/p&gt;

&lt;p&gt;There are less popular alternatives to Sphinx which can also be used for documentation, most notable being &lt;a href="https://readthedocs.org/"&gt;ReadTheDocs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tests
&lt;/h2&gt;

&lt;p&gt;I found writing unit tests in Python easier than in JavaScript due to the availability of mature and built-in tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  unittest
&lt;/h3&gt;

&lt;p&gt;Python has a built-in module &lt;a href="https://docs.python.org/3/library/unittest.html"&gt;&lt;code&gt;unittest&lt;/code&gt;&lt;/a&gt; which can be used to run the tests. The syntax is very similar to Jest or Mocha. Every "test" is a class and every class method is a test case. Methods like &lt;code&gt;setUp()&lt;/code&gt; and &lt;code&gt;teardown()&lt;/code&gt; have special meanings and are equivalent to &lt;code&gt;before&lt;/code&gt; and &lt;code&gt;after&lt;/code&gt; methods seen in JavaScript testing frameworks. Here's a &lt;a href="https://github.com/tarunbatra/password-validator-python/blob/master/tests/test_password_validator.py"&gt;sample test file&lt;/a&gt;. Tests are run by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; unittest discover &lt;span class="nt"&gt;-s&lt;/span&gt; &amp;lt;test_directory&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.pytest.org/en/stable/"&gt;Pytest&lt;/a&gt; and &lt;a href="https://docs.nose2.io/en/latest/"&gt;nose2&lt;/a&gt; are some of the alternatives but I found &lt;code&gt;unittest&lt;/code&gt; adequate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Doctests
&lt;/h3&gt;

&lt;p&gt;Earlier in Documentation section, we discussed about Docstrings. The Examples section in a Docstring provide the user an understanding of how to use the function. What's great is we can also run the example against an automated unit test, using &lt;a href="https://docs.python.org/3/library/doctest.html"&gt;doctest&lt;/a&gt; (another built-in module). This can keep the documentation and implementation in sync.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publishing
&lt;/h2&gt;

&lt;p&gt;Packages can be publushed to PyPi after building. Earlier the builds were made in Egg format, however now it is being replaced by Wheel format. To create a wheel build, we need to install &lt;code&gt;setuptools&lt;/code&gt; and &lt;code&gt;wheel&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;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; setuptools wheel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the build can be created by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 setup.py sdist bdist_wheel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create two files in &lt;code&gt;dist&lt;/code&gt; folder:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One with &lt;code&gt;.tar.gz&lt;/code&gt; extension which is a source archive.&lt;/li&gt;
&lt;li&gt;Another with &lt;code&gt;.whl&lt;/code&gt; extension which is the build file ready to be installed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we need to upload the package build to PyPi. We need &lt;code&gt;twine&lt;/code&gt; for that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; twine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Final step, the uploading of the build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python3 -m twine upload --repository testpypi dist/*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--repository&lt;/code&gt; here lets you select a specific package repository like custom or private npm registeries. It can be configured with a &lt;a href="https://packaging.python.org/specifications/pypirc/"&gt;&lt;code&gt;.pypirc&lt;/code&gt;&lt;/a&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional resources
&lt;/h2&gt;

&lt;p&gt;Two resources which helped me a lot in fuguring out my issues were:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The official &lt;a href="https://packaging.python.org/"&gt;Python Packaging User Guide&lt;/a&gt;. I recommend going through it once.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://realpython.com/"&gt;The Real Python&lt;/a&gt;. I recommend clicking on any link from this website which comes up in search results.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It was an interesting experience for me to tackle all the challenges faced when I was on a journey to publish my first package on PyPi. A mature testing frameworks was a breath of fresh air while dependency management and documentation generation was a daunting task due to all the different tools to manage. In the end I published &lt;a href="https://github.com/tarunbatra/password-validator-python/"&gt;password_validator&lt;/a&gt; and I am happy with the result.&lt;/p&gt;

&lt;p&gt;Do reach out in comments or on my &lt;a href="https://twitter.com/tarunbatra/"&gt;Twitter&lt;/a&gt; if you have any issues with the steps I have mentioned here. Happy publishing!&lt;/p&gt;

</description>
      <category>python</category>
      <category>pip</category>
      <category>npm</category>
      <category>pypi</category>
    </item>
    <item>
      <title>Deploy your website on IPFS: Why and How</title>
      <dc:creator>Tarun Batra</dc:creator>
      <pubDate>Mon, 20 Apr 2020 18:48:20 +0000</pubDate>
      <link>https://dev.to/tbking/deploy-your-website-on-ipfs-why-and-how-mai</link>
      <guid>https://dev.to/tbking/deploy-your-website-on-ipfs-why-and-how-mai</guid>
      <description>&lt;p&gt;I wanted to learn about IPFS and Web 3.0 so I started exploring it and tried to upload my site there. This is an account of the concepts I learnt along the way and how I finally got my site hosted on &lt;a href="http://tbking.eth.link"&gt;tbking.eth&lt;/a&gt;. I'll give a brief intro about IPFS and why hosting static content there makes sense. If you are already familiar with IPFS, you can skip to the hosting part.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is IPFS
&lt;/h2&gt;

&lt;p&gt;Inter-Planetary File System is a decentralized network of shared content. It has a very simple yet interesting design philosophy:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Build a network that works inter planets, and it will be a better network for communication across Earth too.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;IPFS is a decentralized network where peers are connected and share files, in many ways like BitTorrent. The fundamental principle is that unlike the traditional web, where files are served based on their location, in IPFS files are served based on their content. Consider this comparison:&lt;/p&gt;

&lt;p&gt;Google's privacy policy is identified as a file hosted on Google's servers on the address &lt;a href="https://policies.google.com/privacy"&gt;https://policies.google.com/privacy&lt;/a&gt;. The content of the policy doesn't matter. This is called &lt;strong&gt;&lt;code&gt;location-addressing&lt;/code&gt;&lt;/strong&gt;.&lt;br&gt;
On the other hand, IPFS identifies files by their content, using the hash of the file. Let's say you want to read the "&lt;em&gt;XKCD #327 - Exploits of a Mom&lt;/em&gt;". Its IPFS address is &lt;a href="https://ipfs.io/ipfs/QmZVjV5jFV7Jo4Hfj6WPyRnHCxf8kbadkqtQBco2gef64x/"&gt;https://ipfs.io/ipfs/QmZVjV5jFV7Jo4Hfj6WPyRnHCxf8kbadkqtQBco2gef64x/&lt;/a&gt;. This address doesn't depend on the location of the server but the hash. This is called &lt;strong&gt;&lt;code&gt;content-addressing&lt;/code&gt;&lt;/strong&gt;. Whoever cares about XKCD, can host it. This makes &lt;a href="https://en.wikipedia.org/wiki/Link_rot"&gt;broken links&lt;/a&gt; unlikely, which is also one of the goals of IPFS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs-beta.ipfs.io/concepts/"&gt;IPFS docs&lt;/a&gt; explain these concepts well. I recommend them to anyone who wants to dig deeper.&lt;/p&gt;
&lt;h2&gt;
  
  
  Hosting on IPFS
&lt;/h2&gt;

&lt;p&gt;Deploying static content such as personal websites on IPFS is easy. The steps I'm listing below can be used for any file such as a plain HTML file, a website generated by static site generators like Jekyll, Hugo, Hexo, and Gatsby or even a media file. So let's dive in.&lt;/p&gt;
&lt;h3&gt;
  
  
  Adding files
&lt;/h3&gt;
&lt;h4&gt;
  
  
  IPFS Desktop
&lt;/h4&gt;

&lt;p&gt;If you have &lt;a href="https://github.com/ipfs-shipyard/ipfs-desktop"&gt;IPFS Desktop&lt;/a&gt; installed and running, you can add your files using a normal file selector. Just import the directory which has the contents of your static website.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cO11Mz_7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/a8k2247ofkfndqlc2pdr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cO11Mz_7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/a8k2247ofkfndqlc2pdr.png" alt="Adding file in IPFS Desktop" width="880" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Right-click on the newly added content and select &lt;code&gt;Copy CID&lt;/code&gt;. Open &lt;code&gt;https://ipfs.io/ipfs/&amp;lt;cid&amp;gt;&lt;/code&gt; to see our files on the web! 🚀&lt;/p&gt;

&lt;p&gt;Easy right? 😀&lt;/p&gt;

&lt;p&gt;#### IPFS CLI&lt;br&gt;
&lt;a href="https://dist.ipfs.io/#go-ipfs"&gt;IPFS CLI&lt;/a&gt; allows adding of files and directories using &lt;code&gt;add&lt;/code&gt; subcommand.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ipfs add -r ./sample_website
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;added QmaYZE5QQVR3TqQSL5WDcwuxUwBqczbMkmbHeoeNEAumbe sample_website/index.css
added Qme5TzVW4en8HWP49N9nLHwUkAMhCn6zvWteoknmFDhwJg sample_website/index.html
added QmThzr7LgYmUz6tfTArJK5tbrtidXSA12ZCL4ESy6HB8P2 sample_website/index.js
added QmSYDncaMte6GPx5jC7kqeXWnrKsCp4nkLfQXPxiAKtECU sample_website/something.html
added QmeUG2oZvyx4NzfpP9rruKbmV5UNDmTQ8MoxuhTJGVZVTW sample_website
 1.01 KiB / 1.01 KiB [=================================================] 100.00%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hash printed in the last line is the CID for the whole directory and thus our website. We can see the sample website hosted on &lt;a href="https://ipfs.io/ipfs/QmeUG2oZvyx4NzfpP9rruKbmV5UNDmTQ8MoxuhTJGVZVTW/"&gt;https://ipfs.io/ipfs/QmeUG2oZvyx4NzfpP9rruKbmV5UNDmTQ8MoxuhTJGVZVTW/&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TIP: It is important to use relative links in your website since IPFS gateways have a URL which looks like &lt;code&gt;&amp;lt;gateway&amp;gt;/ipfs/&amp;lt;cid&amp;gt;/file.ext&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Pinning
&lt;/h3&gt;

&lt;p&gt;In the last section, we added files to &lt;strong&gt;our&lt;/strong&gt; IPFS node for the network to find. This is why the IPFS gateway was able to resolve it and show it in the browser. But the site will most likely become unreachable once you shut down your IPFS daemon. Even though, after requesting some content on IPFS, the receiving node also becomes a host of the content, but the content will be garbage collected after 12 hours. How to serve your site round the clock in a decentralized web without a server?&lt;/p&gt;

&lt;p&gt;Welcome, &lt;a href="https://docs.ipfs.io/guides/concepts/pinning/"&gt;Pinning&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;A node that pins some content on IPFS hosts it forever (until it unpins it). There are pinning services like &lt;a href="https://pinata.cloud/"&gt;Pinata&lt;/a&gt; which pin the files on their IPFS nodes. This way the website is available always.&lt;br&gt;
In Pinata, you can either upload a file or just provide it's hash if the content has already been uploaded to IPFS. Here's how I pinned the sample website we uploaded above.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E2JG1Lcc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0uiaxnvdb33d27dowx9j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E2JG1Lcc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0uiaxnvdb33d27dowx9j.png" alt="Pinata pinning example" width="880" height="548"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TIP: It is good to pin your site using multiple pinning services for redundancy.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Automated deployments
&lt;/h3&gt;

&lt;p&gt;As you might have noticed already, using IPFS is very easy. I would go as far as to say it's easier than dealing with the traditional web that we use. However, this process has to be repeated every time you want to change your files which is not very ideal. There are tools like &lt;a href="https://fleek.co/"&gt;Fleek&lt;/a&gt; which help in automating all the steps listed above.&lt;/p&gt;

&lt;p&gt;Fleek is like Travis or CircleCi for IPFS deployments. You can link your Github account with it and using the Github hooks, Fleek will trigger a deployment on every push to the Github repository. They also pin everything they deploy.&lt;/p&gt;

&lt;p&gt;So I use Hexo to generate this blog and I was able to add a build step in the Fleek dashboard itself so that I don't need to generate the HTML and push to my repository. Here's the build command that I use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git submodule update &lt;span class="nt"&gt;--recursive&lt;/span&gt; &lt;span class="nt"&gt;--init&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm i &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, I needed to install the submodules myself. They don't do that by default (yet). Check it out, it's super easy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Link to a domain
&lt;/h3&gt;

&lt;p&gt;So now we have our site up and running, but content on IPFS is not as easy to look up as on the traditional web. My site can be found at &lt;a href="https://tarunbatra.com"&gt;https://tarunbatra.com&lt;/a&gt;. But on IPFS, the &lt;em&gt;current version&lt;/em&gt; can be accessed at &lt;a href="https://ipfs.io/ipfs/QmTPTa1ddoSkuakaW56SaL9dicbC71BbwfjRbVjasshCXs/"&gt;https://ipfs.io/ipfs/QmTPTa1ddoSkuakaW56SaL9dicbC71BbwfjRbVjasshCXs/&lt;/a&gt;.&lt;br&gt;
You see the problem. This is not human readable. And the CID changes after every update. There are 2 solutions to this:&lt;/p&gt;

&lt;h4&gt;
  
  
  DNSLink
&lt;/h4&gt;

&lt;p&gt;With &lt;a href="https://docs.ipfs.io/guides/concepts/dnslink/"&gt;DNSLink&lt;/a&gt;, you can point a normal domain to an IPFS content. It can be set up on Fleek very easily. I've pointed &lt;a href="http://ipfs.tarunbatra.com"&gt;ipfs.tarunbatra.com&lt;/a&gt; to the IPFS version using Fleek and you'll be able to open this site.&lt;/p&gt;

&lt;p&gt;IPNS (Inter-Planetary Name Service) also exists and is similar to DNSLink but it's much slower right now.&lt;/p&gt;

&lt;h4&gt;
  
  
  Ethereum Name Service
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://ens.domains/"&gt;ENS&lt;/a&gt; is a naming service that lives on Ethereum blockchain and uses smart contracts to buy domains and set resolver records to them. Since Ethereum is involved, you'd need &lt;a href="https://metamask.io/"&gt;MetaMask&lt;/a&gt; to use it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0xWHcFod--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/irfqn7z1hxxzqrzqrpqq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0xWHcFod--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/irfqn7z1hxxzqrzqrpqq.png" alt="ENS records for tbking.eth" width="880" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I bought the domain &lt;code&gt;tbking.eth&lt;/code&gt; and pointed it to the IPFS content of this site. It still needs to be updated every time I change the contents of my website. I'm not sure if there's any way to automate this yet. However, as of now, Fleek is working on ENS domains integration. I'll update here once that's out. If you know another way, let me know in the comments.&lt;/p&gt;

&lt;p&gt;This was it. IPFS is perfect as the storage layer of the &lt;code&gt;Web 3.0&lt;/code&gt;. Try for yourself, and if you liked this blog, share it and maybe pin it! :)&lt;/p&gt;

</description>
      <category>decentralization</category>
      <category>web3</category>
      <category>ipfs</category>
      <category>ens</category>
    </item>
    <item>
      <title>How to choose between Kafka and RabbitMQ</title>
      <dc:creator>Tarun Batra</dc:creator>
      <pubDate>Fri, 11 Oct 2019 22:30:00 +0000</pubDate>
      <link>https://dev.to/tbking/how-to-choose-between-kafka-and-rabbitmq-4jol</link>
      <guid>https://dev.to/tbking/how-to-choose-between-kafka-and-rabbitmq-4jol</guid>
      <description>&lt;p&gt;Kafka and RabbitMQ – These two terms are frequently thrown around in tech meetings discussing distributed architecture. I have been part of a series of such meetings where we discussed their pros and cons, and whether they fit our needs or not. Here’s me documenting my findings for others and my future self.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Spoiler: We ended up using both for different use-cases.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Message Routing
&lt;/h2&gt;

&lt;p&gt;With respect to message routing capabilities, Kafka is very light. Producers produce messages to topics. Kafka logs the messages in its very simple data structure which resembles… a log! It can scale as much as the disk can. Topics can further have partitions (like sharding). Consumers connect to these partitions to read the messages. Kafka uses a pull-based approach. So, the onus of fetching messages and tracking offsets of read messages lies on consumers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RabbitMQ has very strong routing capabilities&lt;/strong&gt;. It can route the messages through a complex system of exchanges and queues. Producers send messages to exchanges which act according to their configuration. For example, they can broadcast the message to every queue connected with them, or deliver the message to some selected queues, or even expire the messages if not read in a stipulated time. Exchanges can also pass messages to other exchanges, making a wide variety of permutations possible. Consumers can listen to messages in a queue or a pattern of queues. Unlike Kafka, RabbitMQ pushes the messages to the consumers, so the consumers don’t need to keep track of what they have read.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--x7RcXIRg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://tarunbatra.com/data/images/How-to-choose-between-Kafka-and-RabbitMQ/rabbitmq-system.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--x7RcXIRg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://tarunbatra.com/data/images/How-to-choose-between-Kafka-and-RabbitMQ/rabbitmq-system.gif" alt="'RabbitMQ routing example'"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Delivery guarantee
&lt;/h2&gt;

&lt;p&gt;Distributed systems can have 3 delivery semantics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;at-most-once delivery&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In case of failure in message delivery, no retry is done which means data loss can happen, but data duplication can not. This isn’t the most used semantic due to obvious reasons.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;at-least-once delivery&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In case of failure in message delivery, retries are done untill delivery is successfully acknowledged. This ensures no data is lost but this can result in duplicated delivery.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;exactly-once delivery&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Messages are ensured to be delivered exactly once. This is the most desirable delivery semantic and almost &lt;a href="https://bravenewgeek.com/you-cannot-have-exactly-once-delivery/"&gt;impossible to achieve&lt;/a&gt; in a distributed environment.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Both Kafka and RabbitMQ offer at-most-once and at-least-once delivery guarantees.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Kafka provides exactly-once delivery between producer to the broker using idempotent producers (&lt;code&gt;enable.idempotence=true&lt;/code&gt;). Exactly-once message delivery to the consumers is more complex. It is achieved at consumers end by using transactions API and only reading messages belonging to committed transactions (&lt;code&gt;isolation.level=read_committed&lt;/code&gt;). To truly achieve this, consumers would need to avoid non-idempotent processing of messages in case a transaction has to be aborted which is not always possible. &lt;strong&gt;So, Kafka transactions are not very useful in my opinion.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In RabbitMQ, exactly-once delivery is not supported due to the combination of complex routing and the push-based delivery. Generally, it’s recommended to use at-lease-once delivery with idempotent consumers.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;br&gt;NOTE: Kafka Streams is an example of truely idempotent system, which it achieves by eliminating non-idempotent operations in a transaction. It, however is out of the scope of this article. I recommend reading &lt;a href="https://www.confluent.io/blog/enabling-exactly-once-kafka-streams/" rel="noopener"&gt;“Enabling Exactly-Once in Kafka Streams” by Confluent&lt;/a&gt; if you want to dig in it further.&lt;br&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Throughput
&lt;/h2&gt;

&lt;p&gt;Throughput of message queues depends on many variables like message size, number of nodes, replication configuration, delivery guarantees, etc. I will be focussing on the speed of messages produced versus consumed. The two cases which arise are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Queue is empty due to messages being consumed as and when they are produced.&lt;/li&gt;
&lt;li&gt;Queue is backed up due to consumers being offline or producers being faster than consumers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RabbitMQ stores the messages in DRAM for consumption. In the case where consumers are not far behind, the messages are served quickly from the DRAM. Performance takes a hit when a lot messages are unread and the queue is backed up. In this case, the messages have to be read from the disk, which is slower. So, RabbitMQ works faster with empty queues.&lt;/p&gt;

&lt;p&gt;Kafka uses sequential disk I/O to read chunks of the log in an orderly fashion. Performance improves further in case fresh messages are being consumed, as the messages are served from OS page cache without any I/O reads. However, it should be noted that implementing transactions as discussed in last section will have &lt;em&gt;negative effect&lt;/em&gt; on the throughput.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Overall, &lt;strong&gt;Kafka can process millions of messages in a second and is faster than RabbitMQ&lt;/strong&gt;. Whereas, RabbitMQ can process upwards of 20k messages per second.&lt;/p&gt;
&lt;/blockquote&gt;




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

&lt;p&gt;RabbitMQ offers complex use-cases which can not be realized with Kafka’s simple architecture. However, Kafka provides higher throughput in almost all cases. Apart from these differences, both of them provide similar capabilities like fault-tolerance, high availability, scalable, etc. Keeping this in mind, we at &lt;a href="https://smallcase.com"&gt;smallcase&lt;/a&gt; used RabbitMQ for &lt;a href="https://medium.com/making-smalltalk/polling-reliably-at-scale-using-dlqs-841512659c8f"&gt;consistent polling in our transactions system&lt;/a&gt; and Kafka for making our notifications system quick and snappy.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://content.pivotal.io/blog/understanding-when-to-use-rabbitmq-or-apache-kafka"&gt;Understanding When to Use RabbitMQ or Apache Kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.mavenhive.in/which-one-to-use-and-when-rabbitmq-vs-apache-kafka-7d5423301b58"&gt;A comparison between RabbitMQ and Apache Kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.confluent.io/blog/transactions-apache-kafka/"&gt;Transactions in Apache Kafka&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://arxiv.org/abs/1709.00333"&gt;Kafka versus RabbitMQ&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>comparison</category>
      <category>rabbitmq</category>
      <category>kafka</category>
      <category>messagequeue</category>
    </item>
    <item>
      <title>Error messages in login process: Privacy and Security</title>
      <dc:creator>Tarun Batra</dc:creator>
      <pubDate>Sat, 28 Apr 2018 19:14:03 +0000</pubDate>
      <link>https://dev.to/tbking/error-messages-in-login-process-privacy-and-security-4fin</link>
      <guid>https://dev.to/tbking/error-messages-in-login-process-privacy-and-security-4fin</guid>
      <description>&lt;p&gt;Most of us, while developing sites swear to the rule of making error messages shown to the end-user as specific as possible, which goes a long way in creating a friendly UX. But the same rule if used in the login flow, can have significant privacy and security implications. On entering wrong login credentials, most sites show variations of &lt;code&gt;Invalid username/password&lt;/code&gt; error at the login screen.&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%2Ftarunbatra.com%2Fdata%2Fimages%2FError-messages-in-login-process-Privacy-and-Security%2Ftwitter-github-spotify.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%2Ftarunbatra.com%2Fdata%2Fimages%2FError-messages-in-login-process-Privacy-and-Security%2Ftwitter-github-spotify.png" alt="'Twitter, Github and Spotify's login error messages'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It can be safely assumed that no user for such emails exists, yet the error messages shown by these prominent sites don’t indicate that. Do you see why?&lt;/p&gt;

&lt;h2&gt;
  
  
  Protecting user base information
&lt;/h2&gt;

&lt;p&gt;On feeding random emails, a simple login/reset password may show that the user doesn’t exist and therefore no login/reset password is possible. A hostile client may create a list of valid users from this information. Not every site cares about it though. Facebook doesn’t.&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%2Ftarunbatra.com%2Fdata%2Fimages%2FError-messages-in-login-process-Privacy-and-Security%2Ffacebook.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%2Ftarunbatra.com%2Fdata%2Fimages%2FError-messages-in-login-process-Privacy-and-Security%2Ffacebook.png" alt="'Facebook login error message'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It doesn’t hurt for a site like Facebook to leak the user base because everyone’s on it, but when the site is something like Ashley Maddison it can have implications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Closing the loop
&lt;/h3&gt;

&lt;p&gt;People often argue that the user base is anyways leaked during the registration process, which can’t continue if the user already exists. This leak is avoidable too, by continuing the registration through an email. If the user already exists the email may say so, otherwise it may direct the receiver to continue the registration process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deterrence to brute-force
&lt;/h2&gt;

&lt;p&gt;In a brute-force attack, large number of combinations of usernames and passwords are tried to get into a user’s account. A dictionary attack is faster and better as it takes advantage of human tendencies of using common passwords and re-using old passwords. Specific messages like &lt;code&gt;Invalid username&lt;/code&gt; make these attacks faster by eliminating large number of combinations in a few tries.&lt;/p&gt;

&lt;h3&gt;
  
  
  Demo
&lt;/h3&gt;

&lt;p&gt;To quantify the difference detailed error messages can make to brute-force or dictionary attacks, I created a script, &lt;a href="https://github.com/tarunbatra/login-attack-demo" rel="noopener noreferrer"&gt;login-attack-demo&lt;/a&gt; which included:&lt;/p&gt;

&lt;h4&gt;
  
  
  Dictionary
&lt;/h4&gt;

&lt;p&gt;A collection of 1575 most commonly used passwords based on &lt;a href="https://github.com/danielmiessler/SecLists/blob/master/Passwords/probable-v2-top1575.txt" rel="noopener noreferrer"&gt;danielmiessler/SecLists&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Naive server
&lt;/h4&gt;

&lt;p&gt;A server which gives out specific error messages based on the failing stage of the login process. If the username is invalid, it’d say &lt;code&gt;Invalid username&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Smart server
&lt;/h4&gt;

&lt;p&gt;A server which gives out vague error messages on failure of the login process. If the password is invalid and not the username, it’d still say &lt;code&gt;Invalid username/password&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Attacker
&lt;/h4&gt;

&lt;p&gt;A hostile client which uses the dictionary to generate a pair of username and password and uses them to break into the system. It also analyzes the error messages on the following rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An error message saying the password was invalid, indicates the username was a valid one.&lt;/li&gt;
&lt;li&gt;An error message saying the username was invalid, indicates no combination of password will result in a successful login.&lt;/li&gt;
&lt;li&gt;An inconclusive error message is ignored and the next combination is tried.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Results
&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%2Ftarunbatra.com%2Fdata%2Fimages%2FError-messages-in-login-process-Privacy-and-Security%2Fchart.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%2Ftarunbatra.com%2Fdata%2Fimages%2FError-messages-in-login-process-Privacy-and-Security%2Fchart.png" alt="'Analysis chart'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The blue points indicate the number of tries it took the attacker to break into the user’s account with vague error messages, while the red ones are when the error messages were specific. The yellow line is the max number of combinations to try.&lt;/p&gt;

&lt;p&gt;It is evident that in theory vague error messages make it quite tough for the attacker, sometimes as much as 1000 times tougher. However, plugging all the holes may make the UX far from ideal. In the end, it all comes to trade off between UX and security. Depending on the space you’re operating, such methods can be used to safeguard the users.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Originally published &lt;a href="https://tarunbatra.com/blog/security/Error-messages-in-login-process-Privacy-and-Security" rel="noopener noreferrer"&gt;here at tarunbatra.com&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>security</category>
      <category>architecture</category>
      <category>web</category>
      <category>privacy</category>
    </item>
  </channel>
</rss>
