<?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: Can Kahraman</title>
    <description>The latest articles on DEV Community by Can Kahraman (@burakcank).</description>
    <link>https://dev.to/burakcank</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%2F637158%2F619178fc-ae09-4ec9-82e0-9cdab87dad96.jpg</url>
      <title>DEV Community: Can Kahraman</title>
      <link>https://dev.to/burakcank</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/burakcank"/>
    <language>en</language>
    <item>
      <title>Flask configuration</title>
      <dc:creator>Can Kahraman</dc:creator>
      <pubDate>Tue, 25 Jul 2023 15:27:47 +0000</pubDate>
      <link>https://dev.to/burakcank/flask-configuration-12mg</link>
      <guid>https://dev.to/burakcank/flask-configuration-12mg</guid>
      <description>&lt;h1&gt;
  
  
  Configuration hell
&lt;/h1&gt;

&lt;p&gt;Sometimes it's the trivial parts that confuses me. They are being portrayed as "don't spend too much time on it". However a structured configuration might really save you some headaches.&lt;/p&gt;

&lt;p&gt;After realizing that I had a few harmless config differences on my prod env, being accumulated from different sources like docker-compose, or dockerfile and env vars, I have been meaning to change my flask app's config structure for some time now and I finally found time to do it because I started to assume things. And it's dangerous if you are starting to assume things. Having a single source of truth for your config variables is important to me. Otherwise you get lost in the config sea, trying to understand which config variable gets overwritten/ignored by which file.&lt;/p&gt;

&lt;p&gt;Before this refactor I have been using &lt;code&gt;python-dotenv&lt;/code&gt; and a python file called &lt;code&gt;config.py&lt;/code&gt; with multiple separated classes such as &lt;code&gt;BaseConfig&lt;/code&gt;, &lt;code&gt;DevConfig&lt;/code&gt; and &lt;code&gt;ProdConfig&lt;/code&gt; etc. to manage different configurations for different environments. At first I knew what I was doing, but at some point, it started to get complicated and I began to mix things up and &lt;strong&gt;what&lt;/strong&gt; belonged &lt;strong&gt;where&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to choose the right config file format?
&lt;/h2&gt;

&lt;p&gt;There are several configuration file formats out there, such as JSON, YAML, TOML, ini or .env files and similar. As you can see we don't have the perfect solution, and it looks like different library developers have different ideas regarding how the perfect configuration file should look like.&lt;/p&gt;

&lt;p&gt;For one thing, JSON files are missing comments, which sounded like a deal-breaker for me initially. TOML files become overly verbose when it comes to too much nested data. Don't know about YAML, maybe it's the choice for you. There are different opinions on each one, search for the one that suits you.&lt;/p&gt;

&lt;p&gt;Personally the decisions I'm taking ultimately come down to how simple the tool is. Start with the simple and try out more complicated ones as you need them. Therefore I went with JSON, even though it doesn't allow comments. That's fine. If I really crave for comments, maybe I can think of another solution then. But as I said, stick to your simple solution unless you absolutely can't do without.&lt;/p&gt;

&lt;h2&gt;
  
  
  Previously on "Configuration Hell"
&lt;/h2&gt;

&lt;p&gt;I had the following structure when it came to my configuration. I'm using the factory pattern in flask to make the process of creating an app with different configurations easier. This method also simplifies unit testing immensely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# app/__init__.py

from flask import Flask

def create_app(config_type: str = "DevConfig") -&amp;gt; Flask:
    app = Flask(__name__)

    # Load the configuration.
    app.config.from_object(f"config.{config_type}")
    ...

    return app
&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;# config.py

class Config:
    """
    Common settings for all environments.
    """
    STATIC_FOLDER = pathlib.Path(__file__).parent / "app" / "static"
    UPLOAD_FOLDER = STATIC_FOLDER / "uploads"
    PDF_FOLDER = STATIC_FOLDER / "pdfs"
    THUMBNAILS_FOLDER = STATIC_FOLDER / "thumbnails"
    MAX_CONTENT_LENGTH = 16 * 1000 * 1000
    TESTING = False
    ...

class DevConfig(Config):
    SQLALCHEMY_DATABASE_URI = os.environ.get("SQLALCHEMY_DATABASE_URI", "mysql+pymysql://user:pwd@mariadb:3306/mydatabase")
    ...

class ProdConfig(Config):
    SQLALCHEMY_DATABASE_URI = os.environ["SQLALCHEMY_DATABASE_URI"]
    ...

class TestConfig(Config):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = os.environ.get("SQLALCHEMY_DATABASE_URI", "sqlite:///")
    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Config&lt;/code&gt; class holds the common configuration variables that won't change from env to env. And the following classes respectively, hold configuration variables that are specific to their environments.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;confusion&lt;/strong&gt; I'm having here is, when do I need to feed the env variables? When I'm developing on dev, there could be some sane defaults. When I'm deploying to prod, those sane defaults should definitely be ignored because they are specific to dev environment. I also want the application to yell at me when I'm on production and I haven't supplied the database connection string for instance. There mustn't be any default values there and it &lt;strong&gt;has to be&lt;/strong&gt; supplied &lt;em&gt;conciously&lt;/em&gt;. There are also several different config possibilities depending on whether I'm working with docker or not. I want to see each of them clearly and detect the differences with a single glance, instead of looking under docker-compose, or looking under aws task definition or several locations at the same time, trying to merge them in my head.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refactor
&lt;/h2&gt;

&lt;p&gt;In order to make things clear, I created an &lt;code&gt;instance&lt;/code&gt; folder to hold all of my json configurations. This folder will include configurations for all the environments, that is &lt;code&gt;dev.json&lt;/code&gt;, &lt;code&gt;prod.json&lt;/code&gt;, &lt;code&gt;docker-dev.json&lt;/code&gt;, &lt;code&gt;docker-prod.json&lt;/code&gt;, &lt;code&gt;test.json&lt;/code&gt; and finally an example file called &lt;code&gt;config.json&lt;/code&gt;, which should behave as a template for all the other files.&lt;/p&gt;

&lt;p&gt;We will commit the &lt;code&gt;config.json&lt;/code&gt; file to the version control system because it doesn't contain any sensitive information in it, just some fake data. Additionally, I also committed the &lt;code&gt;test.json&lt;/code&gt; into the VC because it doesn't contain secrets either in my case. I use it to run my unit/integration tests on github actions.&lt;/p&gt;

&lt;p&gt;Now my instance folder looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--62utKNum--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7902zgdziye6clluv1se.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--62utKNum--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7902zgdziye6clluv1se.png" alt="instance folder" width="497" height="138"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And my factory function looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# app/__init__.py

def create_app(env: Optional[str] = "prod") -&amp;gt; Flask:
    """Create flask app with the given configuration."""
    app = Flask(__name__, instance_relative_config=True)

    # Load the configuration.
    app.config.from_object("config.Config")
    app.config.from_file(f"{env}.json", load=json.load)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is my config.py file. This file will be here to hold some logic for configuration. I don't want to hardcode file paths, I want them to be relative to the &lt;code&gt;config.py&lt;/code&gt; in the repo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# config.py

import pathlib


class Config:
    # Upload folder under static.
    STATIC_FOLDER = pathlib.Path(__file__).parent / "app" / "static"
    UPLOAD_FOLDER = STATIC_FOLDER / "uploads"
    PDF_FOLDER = STATIC_FOLDER / "pdfs"
    THUMBNAILS_FOLDER = STATIC_FOLDER / "thumbnails"
    MAX_CONTENT_LENGTH = 16 * 1000 * 1000

    # Enable this in order to echo all the sql commands to the db.
    # SQLALCHEMY_ECHO = True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the &lt;code&gt;instance/config.json&lt;/code&gt; file that behaves as an example template for all other config files.&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="nl"&gt;"SECRET_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"verysecret"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"PERMANENT_SESSION_LIFETIME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"GOTENBERG_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://gotenberg:3000/forms/libreoffice/convert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"SQLALCHEMY_DATABASE_URI"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mysql+pymysql://user:pwd@localhost:3306/mydatabase"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ADOBE_PDF_EMBED_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"ALLOWED_EXTENSIONS"&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="s2"&gt;"txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"jpeg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"gif"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"ppt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"csv"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"xls"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"xlsx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"docx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"doc"&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;"TESTING"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"WTF_CSRF_ENABLED"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"MAIL_USERNAME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"example@mail.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;"MAIL_PASSWORD"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"MAIL_SERVER"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"smtp.server.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;"MAIL_PORT"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;587&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"MAIL_USE_TLS"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"MAIL_USE_SSL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"MAIL_DEFAULT_SENDER"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"example@mail.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;"SQLALCHEMY_ECHO"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&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;h2&gt;
  
  
  Gunicorn
&lt;/h2&gt;

&lt;p&gt;By having a separate config file for each env, now I can see exactly what kind of differences I have between the environments. When I want to fire up an app that specifically should use a configuration file called &lt;code&gt;docker-prod.json&lt;/code&gt;, that file should exist and it should contain all of the needed vars under it. This file is the single source of truth for that environment.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CMD gunicorn --bind 0.0.0.0:5000 "app:create_app(env='docker-prod')"&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Some struggles with passing json config to the docker container
&lt;/h2&gt;

&lt;p&gt;Since I'm building my container in the github actions, there's no way for me to include my &lt;code&gt;docker-prod.json&lt;/code&gt; into the container directly. Therefore I have discovered a simple trick, that enables me to encode my config file completely into a single base64 string (which in turn saves me from including all of my env variables one-by-one), put it under github secrets and pull/decode it from there during the build process.&lt;/p&gt;

&lt;p&gt;In the job that I'm currently building my container, I have the following logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Decode docker-prod.json&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo -n "${{ secrets.DOCKER_PROD_JSON }}" | base64 --decode &amp;gt; $GITHUB_WORKSPACE/instance/docker-prod.json&lt;/span&gt;

        &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens here? The checkout actions checks-out the source code and decodes the long base64 string from github secrets, putting it under &lt;code&gt;instance/docker-prod.json&lt;/code&gt;. Eventually when the application starts, I have my config file right where it needs to be without checking it in under version control.&lt;/p&gt;

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

&lt;p&gt;Alright, this is huge for me. Discovering the above trick allowed me to host my prod config free of charge on github secrets. I'm not sure how much it costs to store your secrets in AWS Secret Manager, but yeah...&lt;/p&gt;

&lt;p&gt;Now just by looking at different config files I have in my &lt;code&gt;instance&lt;/code&gt; folder, I instantly see what database connection string I'm using, what email credentials are there, what different api keys I'm making use of in different environments. I don't need to go through multiple config files and try to guess which one supersedes the other.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Notes to self
&lt;/h2&gt;

&lt;p&gt;Maybe this is a naive approach, maybe not. Perhaps you will find some useful points here, perhaps you'll just shrug and say "meh, pretty basic stuff". I'm writing this article for self-document purposes, which should guide me to better practices along the way. What I learned, how I learned, how I applied, how it turned out in the end.&lt;/p&gt;

&lt;p&gt;I'll stick to this for a while and see how it works out.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>flask</category>
    </item>
    <item>
      <title>Visualize Git</title>
      <dc:creator>Can Kahraman</dc:creator>
      <pubDate>Mon, 21 Jun 2021 16:23:40 +0000</pubDate>
      <link>https://dev.to/burakcank/visualize-git-5gid</link>
      <guid>https://dev.to/burakcank/visualize-git-5gid</guid>
      <description>&lt;h1&gt;
  
  
  Dangit Git
&lt;/h1&gt;

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

&lt;p&gt;Yep, sometimes git can be complicated, but that does not mean necessarily it can not be understood unless you are a git expert. You all probably have used git at some point in your development stages and familiar with the basics. Well today I am here to show you the peculiar aspects of this tool and how we can deal with trouble.&lt;/p&gt;




&lt;h2&gt;
  
  
  Visualize stuff baby
&lt;/h2&gt;

&lt;p&gt;Below representation will be our visual work space.&lt;/p&gt;

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

&lt;p&gt;This is our repository and it has several commits and branches in it. First we have an initial commit (root of our tree) at the top, after which we have 3 commits (&lt;strong&gt;&lt;em&gt;added contributors&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;improvements&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;refactor&lt;/em&gt;&lt;/strong&gt;), whose parents are the same. As you see, we also have 3 branches, &lt;strong&gt;&lt;em&gt;master&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;feature1&lt;/em&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;em&gt;feature2&lt;/em&gt;&lt;/strong&gt;. We also have &lt;strong&gt;HEAD&lt;/strong&gt; which we will talk about in a bit.&lt;br&gt;
In order to make it more understandable, I will show you how visual representation changes while we execute the corresponding git commands. By doing so, it is going to allow us to notice the relationship between the commands and the bahavior of git.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is HEAD and HEAD~ etc. ?
&lt;/h2&gt;

&lt;p&gt;We will move &lt;em&gt;HEAD&lt;/em&gt; from time to time in our visual graph, but what is this magical special word to begin with ? Let me explain, &lt;em&gt;HEAD&lt;/em&gt; is nothing more than a pointer in your git workflow, it just marks where you are. That's it. Simply accept &lt;em&gt;HEAD&lt;/em&gt; as an arrow pointing to your location on a map. Now in order to mark other locations, we might need a reference point right ? That is for instance, if I need to explain someone where the commit &lt;em&gt;refactor&lt;/em&gt; is, I can just say go 2 commits above HEAD and there you will find it. Thus &lt;em&gt;HEAD~1&lt;/em&gt; is like saying "the commit above HEAD" and similarly &lt;em&gt;HEAD~2&lt;/em&gt; is equal to saying "the commit 2 steps above HEAD". By the way &lt;em&gt;HEAD~&lt;/em&gt; is the same as &lt;em&gt;HEAD~1&lt;/em&gt; so no fancy stuff there, we programmers are lazy you know.&lt;/p&gt;

&lt;p&gt;In order to prove our point let us execute &lt;code&gt;git checkout feature2&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;HEAD&lt;/em&gt; moved to point to the &lt;em&gt;feature2&lt;/em&gt;, huh well that was simple enough. Get back to master now, we will continue.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is &lt;code&gt;--soft&lt;/code&gt; and &lt;code&gt;--hard&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;These are amongst the most common misunderstood concepts when starting to learn git. I will simply give an illustration to make it more comprehensible. Imagine you are building a tower of cards, like when you were a child and you have a vicious cat pondering around. Suddenly your mom summons you to dinner and you have to go. If you take your tower of cards with you, that is a &lt;em&gt;soft&lt;/em&gt; choice. You don't want your cat to destroy your progress, therefore you take the tower (your changes) with you. On the contrary, if you decide to leave it as is, that vicious cat will definitely tear it down, that's a &lt;em&gt;hard&lt;/em&gt; choice. This hard choice will indefinitely discard all the changes you have done, so use it carefully.&lt;/p&gt;

&lt;p&gt;In summary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git reset --soft HEAD~&lt;/code&gt; - move back one commit and keep your changes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git reset --hard HEAD~&lt;/code&gt; - move back one commit and discard all changes&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  1 - Modify commit ?
&lt;/h2&gt;

&lt;p&gt;You might sometimes commit a work-in-progress and immediately realize you forgot to add something to the commit.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git add&lt;/code&gt; - add the missing/modified file(s) you forgot to add&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git commit --amend --no-edit&lt;/code&gt; - alter the previous commit (remove &lt;code&gt;--no-edit&lt;/code&gt; if you want to modify the commit message)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This will replace your commit with a fresh new one. Now let's see what changed in our visual graph. Oh look at that, only modification was to the &lt;em&gt;hash&lt;/em&gt; of our commit. That means we changed the contents of this commit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmoiqjkgec0x6wlr06eqw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmoiqjkgec0x6wlr06eqw.png" alt="git-3"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  2 - Committed to master instead of a new branch ?
&lt;/h2&gt;

&lt;p&gt;We were working casually and finished our work, only to commit to master instead of another branch. Ah, a silly mistake indeed. You can see our mistake in the graph below. I assure you it has a trivial fix.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git branch feature3&lt;/code&gt; - create a new branch at your current position&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git reset HEAD~ --hard&lt;/code&gt; - take back master to the previous commit&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git checkout feature3&lt;/code&gt; - switch to your new branch&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Let's look at our graph. Okay seems like we now have a branch called &lt;em&gt;feature3&lt;/em&gt; and we successfully recovered our master to where it belongs. &lt;em&gt;HEAD&lt;/em&gt; is pointing to the &lt;em&gt;feature3&lt;/em&gt; branch and that's what we expect from the last action we took above.&lt;/p&gt;
&lt;h2&gt;
  
  
  3 - Committed to the wrong branch !
&lt;/h2&gt;

&lt;p&gt;You made a commit to a wrong branch ? This is a very common scenario and it has an easy fix, unless you have pushed your changes (please tell me you did not). If you haven't pushed your changes yet, hurrraaaay. If you have already pushed your changes, well we'll deal with that later. Let's clear the easier one first.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git reset HEAD~ --soft&lt;/code&gt; - take yourself back to the previous commit and keep your changes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git stash&lt;/code&gt; - stash your changes so we can switch branches&lt;/li&gt;
&lt;/ul&gt;

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

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git checkout feature2&lt;/code&gt; - switch to another branch&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git stash pop&lt;/code&gt; - unpack your changes from stash&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git add .&lt;/code&gt; - add your files and commit them normally&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git commit -m "fixed"&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm5b4qhpfxzyacixlxvgv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm5b4qhpfxzyacixlxvgv.png" alt="git-9"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  4 - I want to revert this specific commit from 10 days before
&lt;/h2&gt;

&lt;p&gt;One of those moments where you need to take back a change from daaaaays ago right ? Git has you covered.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git log&lt;/code&gt; - find the hash of that commit you want to revert&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git revert &amp;lt;hash&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This action creates a revert commit, meaning it does NOT modify any of your previous commits and just creates a new commit with the exact opposite file modifications. This is cool. You can see the reverse commit below, a whole new commit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F99d7j2tyguhe3nafo34w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F99d7j2tyguhe3nafo34w.png" alt="git-10"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  5 - Oh dang, I did something terribly wrong, I lost my commit !
&lt;/h2&gt;

&lt;p&gt;This one is nasty. It is best we visualize this one in order to understand what's coming step by step, because my friend, most of the beginners are afraid of GETTING DETACHED. Ok jokes aside, seriously when I first encountered a similar scenario where I had to go into detached state and did something wrong, I felt terribly lost, even though I was not. That's why we will spend extra effort to make it more transparent.&lt;/p&gt;

&lt;p&gt;Imagine checking out a specific commit, in this case &lt;em&gt;refactor&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git checkout c5b64
Note: checking out 'c5b647'.

You are in 'detached HEAD' state...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Ooookayyy, what does that even mean ? Keep it cool and beware, pretty much nothing is unrecoverable in git except that previously mentioned &lt;strong&gt;hard&lt;/strong&gt; reset or maybe deleting your folder altogether and ending this agony. &lt;strong&gt;&lt;em&gt;Detached HEAD&lt;/em&gt;&lt;/strong&gt; state means you are pointing to a commit directly, instead of pointing to a branch. HUUUH and that's it ? YEAH ! EXPERIMENT TIME ! Since we are pointing to a lonely commit, let us change something and commit it and see what happens.&lt;/p&gt;

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

&lt;p&gt;Seems like we are still in that evil &lt;em&gt;detached HEAD&lt;/em&gt; state. Let's assume we took such an action while not knowing the bits and pieces of git and switched back to master via &lt;code&gt;git checkout master&lt;/code&gt;. Clearly we now know, &lt;code&gt;git checkout master&lt;/code&gt; in its most basic form is like saying "Go ahead and just point to master".&lt;/p&gt;

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

&lt;p&gt;You see, we checked out some unknown commit somewhere in space, made some changes and committed it with the name of "some changes after refactor" and moved back to master, am I correct ? Now, attention please, since you don't know the hash of that "some changes after refactor" commit and it does not have an explicit branch attached to it, you have no way of knowing if it's there. Hence, you think after all your developments and improvements, you lost it all and it's time to start from ground zero. Stop right there because &lt;code&gt;git reflog&lt;/code&gt; is coming for rescue.&lt;/p&gt;

&lt;p&gt;We may inspect the output of this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git reflog
5b35f6d HEAD@{1}: pull ...
ca92d15 HEAD@{2}: ...
759dab1 HEAD@{3}: commit (merge): ...
065e269 HEAD@{4}: commit: ...
f357606 HEAD@{5}: commit: ...
9u7b45 HEAD@{6}: checkout: moving from master to 9u7b45d272867b63d54f96d4aa57f8ecc479cd0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;9u7b45 HEAD@{6}: checkout: moving from master to 9u7b45d272867b63d54f96d4aa57f8ecc479cd0&lt;/code&gt; should give you a pretty rough idea of what you have done, that is, you have moved to a branchless commit in the past. There it is now, your precious little commit that was drifting in space without any label on it. Now go there and put a label on it, or that is to say, create a branch, whatever.&lt;/p&gt;

&lt;h2&gt;
  
  
  I AM DONE WITH GIT !
&lt;/h2&gt;

&lt;p&gt;If you are really done with this insane version control stuff that I have explained, here is a final solution :D&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ..
sudo rm -r ducking-git-repo-dir
git clone https://some.github.url/ducking-git-repo-dir.git
cd ducking-git-repo-dir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Proceeds to copy the working folder to -&amp;gt; "/home/git_bender/last_last_final_version_of_git_tutorial/"&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Inspired from &lt;a href="https://dangitgit.com/" rel="noopener noreferrer"&gt;Dangitgit&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>git</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Want to compress your pdf files ?</title>
      <dc:creator>Can Kahraman</dc:creator>
      <pubDate>Tue, 25 May 2021 14:41:23 +0000</pubDate>
      <link>https://dev.to/burakcank/want-to-compress-your-pdf-files-5ff9</link>
      <guid>https://dev.to/burakcank/want-to-compress-your-pdf-files-5ff9</guid>
      <description>&lt;p&gt;Are you tired of scanning images and trying to shrink them under 25 mbs just so you can send them via email ? Look no further, I am here to save you from this trouble. (that kinda rhymed.)&lt;/p&gt;




&lt;p&gt;This basic shell script uses &lt;em&gt;ghostscript&lt;/em&gt; to compress your scanned pdfs significantly. Just yesterday I scanned 100 pages of documents and it was over 90 mbs. I searched for a way to compress them under 25 mbs and voila. Here I was with only 6 mbs of pdfs. &lt;em&gt;Much wow. Such compression.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Why&lt;/strong&gt; we need it,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fast.&lt;/li&gt;
&lt;li&gt;Ez.&lt;/li&gt;
&lt;li&gt;No need to go online. Especially no need to upload company top-secret documents to any website you see on the Internet.&lt;/li&gt;
&lt;li&gt;Possible huge compression without notable loss.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Command usage:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;./shrinkpdf.sh in.pdf out.pdf&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Don't forget to install &lt;em&gt;ghostscript&lt;/em&gt;.
&lt;/h4&gt;




&lt;p&gt;If you are in a situation exactly like me where there are 100 pdf files under one folder that you want to compress altogether, a simple for loop will suffice.&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="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;

&lt;span class="n"&gt;infolder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&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="n"&gt;outfolder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"compressed_"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;infolder&lt;/span&gt;
&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outfolder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;listdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;infolder&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;"./shrinkpdf.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;infolder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outfolder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run this python script,&lt;/p&gt;

&lt;p&gt;&lt;code&gt;python shrink.py pdfs/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and all of your pdfs will be put under &lt;em&gt;compressed_pdfs/&lt;/em&gt;.&lt;/p&gt;




&lt;p&gt;&lt;a href="http://www.alfredklomp.com/programming/shrinkpdf/"&gt;http://www.alfredklomp.com/programming/shrinkpdf/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All the relevant details and more, inside. I humbly wanted to let you know such a useful tool exists.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>python</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Some thoughts about productivity</title>
      <dc:creator>Can Kahraman</dc:creator>
      <pubDate>Mon, 24 May 2021 22:25:58 +0000</pubDate>
      <link>https://dev.to/burakcank/some-thoughts-about-productivity-308k</link>
      <guid>https://dev.to/burakcank/some-thoughts-about-productivity-308k</guid>
      <description>&lt;p&gt;For the last couple of years, I have been in the Linux space and mostly tried to find the best distro, best file manager, best terminal etc. I was caught up in the loop of finding the best programs to handle a job. This approach, unless controlled, usually leads to low productivity in the long term.&lt;/p&gt;

&lt;p&gt;How so you might ask ? Well, this behavior is really relatable for some people. When I am talking about this subject, some of you might probably say, &lt;em&gt;"Oh yes I have been there"&lt;/em&gt;. In Linux world, there is even a term for this. &lt;em&gt;Ricing&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Ricing means tweaking the configuration of your working environment to suit your personal needs. At particular level, one might consider this useful, and it indeed is. There are some workflow approaches, shortcuts and others that helps the developer access files and folders faster, learn by muscle memory and perform some set of actions quicker. Past that particular level and you are now in the over-ricing zone. Where you are essentially tweaking every aspect of each program, work environment to your needs, but at the same time you are never satisfied so you keep tweaking and changing settings, configs and what not. It reduces your productivity and at the end of the day when you look back, you realize you have done nothing but tweaking some stuff to find the best work environment.&lt;/p&gt;

&lt;p&gt;This is what I call the loop &lt;em&gt;(referring to the Bandersnatch here)&lt;/em&gt;. You are in the &lt;em&gt;loop&lt;/em&gt;. You need to get out and you need to get sh*t done. Don't worry about using the best tool out there, don't try to find the absolute efficient program. Just get it done.&lt;/p&gt;

&lt;p&gt;This is in actuality a paradox. The paradox goes as such, &lt;em&gt;"The more choices you have, the less satisfied you are"&lt;/em&gt;. Seems about right when you think for a bit.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
