<?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: Filip Todic</title>
    <description>The latest articles on DEV Community by Filip Todic (@fitodic).</description>
    <link>https://dev.to/fitodic</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%2F232622%2F41b84e75-df75-482c-b27a-140a512f6651.jpg</url>
      <title>DEV Community: Filip Todic</title>
      <link>https://dev.to/fitodic</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fitodic"/>
    <language>en</language>
    <item>
      <title>How to change PostgreSQL's data directory on Linux</title>
      <dc:creator>Filip Todic</dc:creator>
      <pubDate>Tue, 20 Apr 2021 14:39:54 +0000</pubDate>
      <link>https://dev.to/fitodic/how-to-change-postgresql-s-data-directory-on-linux-2n2b</link>
      <guid>https://dev.to/fitodic/how-to-change-postgresql-s-data-directory-on-linux-2n2b</guid>
      <description>&lt;p&gt;There comes a time when you have to restore a relatively large database locally. Most likely, you've partitioned your disk, and your &lt;code&gt;root&lt;/code&gt; partition got the thick end of it, 50 GB if you were generous. Let's assume that that's not nearly enough for the database you're about to restore. At the same time, your &lt;code&gt;/home&lt;/code&gt; partition got the rest of the disk space you had available.&lt;/p&gt;

&lt;p&gt;You can always resize these two partitions, but then you would have to back up your &lt;code&gt;/home&lt;/code&gt; directory, unmount it or find a Live USB and tamper with them. If you don't have the time, or the desire, or even a backup disc, you can always change the location where &lt;code&gt;postgresql&lt;/code&gt; stores its data.&lt;/p&gt;

&lt;p&gt;The following instructions are a love letter to all those lost souls who find themselves in this situation and forget to check the status of SELinux, as well as to my future self who'll most likely have to do it again. Considering this is mostly a dump of my bash history, I hope this exact procedure works for you. If not, feel free to contact me and we'll update it together.&lt;/p&gt;

&lt;h1&gt;
  
  
  Procedure
&lt;/h1&gt;

&lt;p&gt;A fresh install is the easiest to change, but let's assume you have some databases locally you don't want to lose, just move them and restore the large database next to them. The steps are more or less the same anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;data_directory&lt;/code&gt; and &lt;code&gt;config_file&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Before you start anything, locate the &lt;code&gt;postgresql&lt;/code&gt;'s configuration file and its data directory:&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="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;su - postgres
&lt;span class="o"&gt;[&lt;/span&gt;postgres@host ~]&lt;span class="nv"&gt;$ &lt;/span&gt;psql
Password &lt;span class="k"&gt;for &lt;/span&gt;user postgres:
psql &lt;span class="o"&gt;(&lt;/span&gt;12.6&lt;span class="o"&gt;)&lt;/span&gt;
Type &lt;span class="s2"&gt;"help"&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;help.

&lt;span class="nv"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;# SHOW config_file;&lt;/span&gt;
            config_file
&lt;span class="nt"&gt;-----------------------------------&lt;/span&gt;
 /var/lib/pgsql/data/postgresql.conf
&lt;span class="o"&gt;(&lt;/span&gt;1 row&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;# SHOW data_directory;&lt;/span&gt;
  data_directory
&lt;span class="nt"&gt;-------------------&lt;/span&gt;
 /var/lib/pgsql/data
&lt;span class="o"&gt;(&lt;/span&gt;1 row&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Stop the &lt;code&gt;systemd&lt;/code&gt; service
&lt;/h2&gt;

&lt;p&gt;Stop the &lt;code&gt;postgresql&lt;/code&gt; &lt;code&gt;systemd&lt;/code&gt; service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl stop postgresql.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  New location
&lt;/h2&gt;

&lt;p&gt;Create a directory where you have enough disk space available (in this case, it's the &lt;code&gt;/home&lt;/code&gt; directory), grant the &lt;code&gt;postgres&lt;/code&gt; user ownership and permissions over it and copy the original data directory to the new location (the key is to &lt;a href="https://thecodinginterface.com/blog/postgresql-changing-data-directory/"&gt;preserve the same ownership and permissions structure&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;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /home/pgdata
&lt;span class="nb"&gt;chown &lt;/span&gt;postgres:postgres /home/pgdata
&lt;span class="nb"&gt;chmod &lt;/span&gt;700 /home/pgdata
rsync &lt;span class="nt"&gt;-av&lt;/span&gt; /var/lib/pgsql/data/ /home/pgdata/data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;code&gt;postgresql&lt;/code&gt; configuration
&lt;/h2&gt;

&lt;p&gt;Open the &lt;code&gt;postgresql.conf&lt;/code&gt; file in the new location and update the &lt;code&gt;data_directory&lt;/code&gt; variable, setting it to the new location where your data was moved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vim /home/pgdata/data/postgresql.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;# FILE LOCATIONS&lt;/span&gt;
&lt;span class="c"&gt;#------------------------------------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# The default values of these variables are driven from the -D command-line&lt;/span&gt;
&lt;span class="c"&gt;# option or PGDATA environment variable, represented here as ConfigDir.&lt;/span&gt;

data_directory &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'/home/pgdata/data'&lt;/span&gt;    &lt;span class="c"&gt;# use data in another directory&lt;/span&gt;
                                        &lt;span class="c"&gt;# (change requires restart)&lt;/span&gt;
&lt;span class="c"&gt;#hba_file = 'ConfigDir/pg_hba.conf'     # host-based authentication file&lt;/span&gt;
                                        &lt;span class="c"&gt;# (change requires restart)&lt;/span&gt;
&lt;span class="c"&gt;#ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;code&gt;systemd&lt;/code&gt; configuration
&lt;/h2&gt;

&lt;p&gt;Do the same thing with the &lt;code&gt;postgresql.service&lt;/code&gt;'s &lt;a href="https://www.joe0.com/2020/06/16/postgres-12-how-to-change-data-directory/"&gt;&lt;code&gt;systemd&lt;/code&gt; configuration file&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;vim /lib/systemd/system/postgresql.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;span class="nv"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;PGDATA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/pgdata/data
&lt;span class="c"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you're done editing the &lt;code&gt;systemd&lt;/code&gt; configuration, reload it and start the service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl daemon-reload
systemctl start postgresql.service
systemctl status postgresql.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SELinux
&lt;/h3&gt;

&lt;p&gt;If you're receiving some vague &lt;code&gt;Permission denied&lt;/code&gt; errors, &lt;a href="https://stackoverflow.com/questions/32556589/postgresql-can-not-start-after-change-the-data-directory#comment52970555_32556589"&gt;check whether or not you have SELinux enabled&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;&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/fs/selinux/enforce
1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the result is &lt;code&gt;1&lt;/code&gt;, then SELinux is in &lt;code&gt;enforcing&lt;/code&gt; mode. To temporarily &lt;a href="https://www.golinuxcloud.com/disable-selinux/#Permissive"&gt;set it to &lt;code&gt;permissive&lt;/code&gt; mode&lt;/a&gt; (&lt;code&gt;0&lt;/code&gt;), run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;setenforce 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can try starting the &lt;code&gt;postgresql.service&lt;/code&gt; again. If the process has started successfully, stop it, and tell SELinux to &lt;a href="https://serverfault.com/a/809364"&gt;apply the same context to the new location&lt;/a&gt;. Then you can return SELinux to &lt;code&gt;enforcing&lt;/code&gt; mode&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;semanage fcontext &lt;span class="nt"&gt;--add&lt;/span&gt; &lt;span class="nt"&gt;--equal&lt;/span&gt; /var/lib/pgsql /home/pgdata
restorecon &lt;span class="nt"&gt;-rv&lt;/span&gt; /home/pgdata
setenforce 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you should be able to start the &lt;code&gt;postgresql.service&lt;/code&gt; without any errors&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl start postgresql.service
systemctl status postgresql.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Confirmation
&lt;/h2&gt;

&lt;p&gt;To confirm the new location and configuration is used, rerun the first step:&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;sudo &lt;/span&gt;su - postgres
&lt;span class="o"&gt;[&lt;/span&gt;postgres@lenovo ~]&lt;span class="nv"&gt;$ &lt;/span&gt;psql
Password &lt;span class="k"&gt;for &lt;/span&gt;user postgres:
psql &lt;span class="o"&gt;(&lt;/span&gt;12.6&lt;span class="o"&gt;)&lt;/span&gt;
Type &lt;span class="s2"&gt;"help"&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;help.

&lt;span class="nv"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;# SHOW data_directory;&lt;/span&gt;
  data_directory
&lt;span class="nt"&gt;-------------------&lt;/span&gt;
 /home/pgdata/data
&lt;span class="o"&gt;(&lt;/span&gt;1 row&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;# SHOW config_file;&lt;/span&gt;
            config_file
&lt;span class="nt"&gt;-----------------------------------&lt;/span&gt;
 /home/pgdata/data/postgresql.conf
&lt;span class="o"&gt;(&lt;/span&gt;1 row&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>beginners</category>
      <category>tutorial</category>
      <category>linux</category>
      <category>database</category>
    </item>
    <item>
      <title>Automate it before you document it</title>
      <dc:creator>Filip Todic</dc:creator>
      <pubDate>Fri, 13 Nov 2020 10:28:11 +0000</pubDate>
      <link>https://dev.to/fitodic/automate-it-before-you-document-it-4e15</link>
      <guid>https://dev.to/fitodic/automate-it-before-you-document-it-4e15</guid>
      <description>&lt;p&gt;I help clients fix their legacy software for a living, and one of the most common complaints (or regrets) I hear is "If only we had documented this &lt;em&gt;pain-in-the-a*s-service&lt;/em&gt;, we wouldn't have had these problems". Despite the best intentions, this is a common misconception and a dangerous generalization that produces more harm than good in the long run.&lt;/p&gt;

&lt;p&gt;First of all, not all software documentation is created equal. To subject the project, customer and internal documentation to the same standards and scrutiny would be a massive waste of time and energy because they serve different purposes. The format and contents of a document depend mostly on the intended audience. For example, if you're a developer writing libraries, the developers using your libraries are your audience. Those developers create services and products on top of those libraries, and their customers are their audience. These two types of audiences share a common goal, to get up and running as fast as possible. This is often achieved by creating a system that is intuitive for your audience to use, simple or straightforward to integrate, and documented and configurable in a way that avoids cognitive overload.&lt;/p&gt;

&lt;p&gt;Second of all, any form of documentation runs the risk of becoming outdated, especially the internal documentation. Documentation pertaining to onboarding, project setup, library creation, testing, builds and deployment oftentimes comprises various steps that need to be executed in sequence to achieve the same result. Those are ideal candidates for automation. Ideally, you'd have a single command that encapsulates all those steps that were previously documented, and document the invocation example(s) with a short description of what the command does. If a procedure changes in the future, one person can update it, and the rest of the team can continue using with the same effect. For example, I've seen documents outlining the steps necessary to create a Python library instead of using a &lt;a href="https://github.com/ionelmc/cookiecutter-pylibrary"&gt;template&lt;/a&gt;, as well as documents describing how to set up your environment to run tests and deploy libraries, tasks easily &lt;a href="https://fitodic.github.io/python-package-distribution-can-be-easy"&gt;automated&lt;/a&gt; using &lt;a href="https://tox.readthedocs.io/en/latest/"&gt;&lt;code&gt;tox&lt;/code&gt;&lt;/a&gt;. Not only does automation reduce the amount of documentation you need to write and keep up-to-date, but decreases the surface area of your system, making it easier to use and maintain.&lt;/p&gt;

&lt;p&gt;On the other hand, an alternative where nothing is automated and everything is documented is a lose-lose situation, both for business owners and employees. Considering that some, if not most, projects are &lt;a href="https://nibblestew.blogspot.com/2020/10/is-your-project-unique-snowflake-or-do.html"&gt;unique snowflakes&lt;/a&gt;, employees can experience micro-frustrations on a daily basis just by using or switching between different projects. Each project can have a slightly different set of steps to achieve the same effect (e.g. run the test suite or deploy), none of which are automated, but some of them are documented. That means they are wasting valuable time on repetitive and monotonous tasks instead of producing something of value to the customers. That's why it's in the business owner's or manager's interest to invest time in automating everything that can be automated, instead of documenting it and hoping for the best.&lt;/p&gt;

&lt;p&gt;In summary, documentation and automation have their advantages when applied in the right circumstances. Neither is a silver bullet, so don't treat it as such. The more we automate, the more time we'll have to focus on the things that really matter. As a consequence, the end result will be simpler, easier to user, and require far less documentation.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>programming</category>
      <category>beginners</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>It's just business</title>
      <dc:creator>Filip Todic</dc:creator>
      <pubDate>Mon, 21 Sep 2020 09:20:55 +0000</pubDate>
      <link>https://dev.to/fitodic/it-s-just-business-2jok</link>
      <guid>https://dev.to/fitodic/it-s-just-business-2jok</guid>
      <description>&lt;p&gt;As I was making breakfast this morning, a social media post about why developers keep autoplaying videos on their sites was brought to my attention. There were some fair counterarguments in the comments section about the clients' wishes, and some less constructive arguments that can be categorized under the "Just say no!" category.&lt;/p&gt;

&lt;p&gt;Prior to that splash of investigative "wisdom", I saw an &lt;a href="https://twitter.com/QuinnyPig/status/1306427431260020736/photo/1"&gt;existential question being posted on Hacker News&lt;/a&gt;. Putting aside how incongruous this last sentence sounds (not to mention the post), I asked myself what makes programmers think they are so special?&lt;/p&gt;

&lt;p&gt;I know some programmers think of themselves as craftsmen or artists, as if their work is somehow a work of art. Newsflash, &lt;em&gt;it's not&lt;/em&gt;. First of all, most artists become famous after they're dead, which obviously makes their ability to reap the rewards of their work particularly hard. Now imagine a world where legacy software becomes invaluable because the author or the entire team have just kicked the bucket. You can almost hear their colleagues saying "there will never be another to-do list like that ever again".&lt;/p&gt;

&lt;p&gt;Unfortunately, these "artists" will never miss an opportunity to quote Spiderman's uncle, and say that we as programmers have great power and great responsibility. You can always say "no" to your clients and employers whenever you have to do something that hurts your delicate sensibilities. In theory, yes. In practice, it's a little bit different, but we're getting there.&lt;/p&gt;

&lt;p&gt;Let's say you've done your homework and prepared and air-tight case for your employer that goes beyond the regular "I don't like this feature". Unless you present an alternative that makes financial sense to the business (finance &lt;em&gt;is&lt;/em&gt; the &lt;a href="https://en.wikipedia.org/wiki/Esperanto"&gt;Esperanto&lt;/a&gt; of the real world), it's either you, or the feature.&lt;/p&gt;

&lt;p&gt;I know this sounds a bit cynical, but that's not my intention. It's also not an excuse to turn a blind eye on unethical behavior that should be stopped. It's just how the world works. Unless the thing you rebel against is not causing substantial damage to the business or its users in a way that harms the business, you'll be replaced with someone who &lt;em&gt;can&lt;/em&gt; and &lt;em&gt;will&lt;/em&gt; do it because there's rent to pay or just doesn't care.&lt;/p&gt;

&lt;p&gt;If you really want to compare this line of work with another industry in hopes of determining your position in the hierarchy, please stop using arts, construction and manufacturing industries when there are &lt;a href="https://community.aiim.org/blogs/daniel-oleary/2012/07/08/drug-dealers-and-it-are-the-only-people-who-call-their-customers-users"&gt;far better analogies&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Drug Dealers and IT are the only people who call their customers “users”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's not a coincidence. If you've never seen &lt;a href="https://en.wikipedia.org/wiki/The_Wire"&gt;The Wire&lt;/a&gt;, I suggest you take a good hard look and see where you would fit in the supply chain or the society at large. My guess, programmers would be the ones who cut the raw product and mix it with various other substances to produce different versions of the product (or the beat cops/task forces if the first option is too denigrating to you), but that's just me. Either way, the programmer's impact on the company's policy is limited by design, so don't expect autoplaying videos will disappear en masse.&lt;/p&gt;

&lt;p&gt;As to existential questions, neither your title nor your salary makes you special. Programmers are paid well because there is a high demand for a certain set of skills, and a low supply. It may change over time, it may not. In the meantime, find a hobby and relax. It's just business.&lt;/p&gt;

</description>
      <category>career</category>
      <category>watercooler</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Controlling access to files uploaded by users</title>
      <dc:creator>Filip Todic</dc:creator>
      <pubDate>Wed, 25 Mar 2020 19:46:00 +0000</pubDate>
      <link>https://dev.to/fitodic/controlling-access-to-files-uploaded-by-users-3php</link>
      <guid>https://dev.to/fitodic/controlling-access-to-files-uploaded-by-users-3php</guid>
      <description>&lt;p&gt;Imagine a situation where you have to check whether or not a user that sent the request can access or download files that were uploaded by another user. Perhaps user A uploaded a file that needs to be shared only with user B or only with authenticated users. If your application is deployed behind a &lt;a href="https://en.wikipedia.org/wiki/Reverse_proxy"&gt;reverse-proxy&lt;/a&gt; such as &lt;a href="https://www.nginx.com/resources/wiki/"&gt;&lt;code&gt;nginx&lt;/code&gt;&lt;/a&gt;, you can use the best of both worlds: your application for checking the user’s permissions and the Web server for serving the files the application tells it to serve.&lt;/p&gt;

&lt;p&gt;Before we begin, there are a couple of things I would like to address. First of all, having your Web application serve the media files by loading it into memory and sending it in a response is &lt;a href="https://docs.djangoproject.com/en/dev/howto/static-files/#serving-static-files-during-development"&gt;grossly inefficient&lt;/a&gt;. You may not know the size of the file, or there could be many requests happening at once. Whatever the case may be, there is a better way.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;X-Accel&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;To quote the &lt;a href="https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/"&gt;official documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;X-accel allows for internal redirection to a location determined by a header returned from a backend.&lt;/p&gt;

&lt;p&gt;This allows you to handle authentication, logging or whatever else you please in your backend and then have NGINX handle serving the contents from redirected location to the end user, thus freeing up the backend to handle other requests. This feature is commonly known as &lt;a href="https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/"&gt;&lt;code&gt;X-Sendfile&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To achieve this, at least two things have to be implemented:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The application’s response must contain the &lt;code&gt;X-Accel-Redirect&lt;/code&gt; header;&lt;/li&gt;
&lt;li&gt;The location should be marked as &lt;code&gt;internal;&lt;/code&gt; to prevent direct access to the URI.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;X-Accel-Redirect&lt;/code&gt; header
&lt;/h3&gt;

&lt;p&gt;This header tells &lt;code&gt;nginx&lt;/code&gt; which URI to serve. Although the following example uses &lt;a href="https://www.django-rest-framework.org/"&gt;&lt;code&gt;django-rest-framework&lt;/code&gt;&lt;/a&gt;, the same thing can be achieved with any other Web framework.&lt;/p&gt;

&lt;p&gt;If we assume all files uploaded by users are located in the &lt;code&gt;/home/user/repo/media/&lt;/code&gt; directory (also defined in Django’s &lt;a href="https://docs.djangoproject.com/en/dev/ref/settings/#media-root"&gt;&lt;code&gt;MEDIA_ROOT&lt;/code&gt;&lt;/a&gt; setting), or more precisely, the &lt;code&gt;/home/user/repo/media/files/{user.id}/&lt;/code&gt; directory by the &lt;code&gt;FileField&lt;/code&gt;’s &lt;a href="https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.FileField.upload_to"&gt;&lt;code&gt;upload_to&lt;/code&gt;&lt;/a&gt; function, the view looks something 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;from pathlib import Path

from django.conf import settings
from django.http import HttpResponseRedirect

from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet

from .models import File
from .permissions import CanAccessFile

class FileViewSet(ModelViewSet):
    permission_classes = [CanAccessFile]
    queryset = File.objects.all()

    @action(detail=True, methods=["get"])
    def download(self, request, pk=None):
        obj = self.get_object()
        if settings.DEBUG:
            return HttpResponseRedirect(obj.upload.url)

        file_name = Path(obj.upload.path).name
        headers = {
            "Content-Disposition": f"attachment; filename={file_name}",
            "X-Accel-Redirect": (
                f"/uploads/files/{obj.user_id}/{file_name}"
            ),
        }
        return Response(data=b"", headers=headers)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the &lt;code&gt;settings.DEBUG&lt;/code&gt; block is here so developers can keep using &lt;a href="https://docs.djangoproject.com/en/dev/howto/static-files/#serving-files-uploaded-by-a-user-during-development"&gt;Django’s &lt;code&gt;static&lt;/code&gt; mechanism for serving media files during development&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are also &lt;a href="https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/#special-headers"&gt;other &lt;code&gt;X-Accel-*&lt;/code&gt; headers&lt;/a&gt; that can be set by the application to further refine the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;internal&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The application’s response that contains the &lt;code&gt;X-Accel-Redirect&lt;/code&gt; header is picked up by the Web server on its way back to the client. In order for &lt;code&gt;nginx&lt;/code&gt; to locate the file that should be sent to the client, the configuration should look something 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;server {
    server_name example.com;

    location /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/user/repo;
    }

    location /uploads/ {
        internal;
        alias /home/user/repo/media/;
    }

    location / {
        include /etc/nginx/proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that all set, you’re ready to start serving files to select users!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>nginx</category>
      <category>django</category>
    </item>
    <item>
      <title>Python package distribution can be easy</title>
      <dc:creator>Filip Todic</dc:creator>
      <pubDate>Sun, 26 Jan 2020 10:26:00 +0000</pubDate>
      <link>https://dev.to/fitodic/python-package-distribution-can-be-easy-4f3f</link>
      <guid>https://dev.to/fitodic/python-package-distribution-can-be-easy-4f3f</guid>
      <description>&lt;p&gt;There have been so &lt;a href="https://www.youtube.com/watch?v=AQsZsgJ30AE&amp;amp;feature=youtu.be&amp;amp;t=50"&gt;many blog posts and presentations about Python packaging&lt;/a&gt; that I’m reluctant to write what could be interpreted as &lt;em&gt;yet another one&lt;/em&gt;, but I’ll give it a go. Why? Because up until recently, I was working on several interconnected libraries, each of which had a more ridiculous build process than the next.&lt;/p&gt;

&lt;p&gt;Needless to say, each had its own deployment procedure. Some were documented, others were left to the author’s interpretation. Dozens of commands to execute. Some had to be built locally because the CI was apparently “too low on resources”. All of this for pure Python packages. No C extensions. It’s like saying “our machine doesn’t have the resources to create a ZIP file”.&lt;/p&gt;

&lt;p&gt;Not to mention libraries that had themselves listed as build dependencies, or &lt;code&gt;setup.py&lt;/code&gt; reading the virtual environment’s environment variables to determine which version of a particular dependency should be installed. Or git branching workflows so complicated that made resolving merge conflicts a routine, with the changelog being the worst of it.&lt;/p&gt;

&lt;p&gt;If you’re a pragmatic developer who has better things to do with its time than watching broken processes in action, you would have searched for a better way. And you would have found one after a quick search and a few mouse clicks away because these issues have already been addressed and solved by the community.&lt;/p&gt;

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

&lt;p&gt;When I started looking for a solution, I wanted to achieve three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Have a single command for performing all the package validity checks: build, test suite, linting, documentation, etc.&lt;/li&gt;
&lt;li&gt;Have a single command for deployment where the developer specifies the next version and it magically appears on PyPI;&lt;/li&gt;
&lt;li&gt;Make the aforementioned processes easy to debug, modify and replace if a better option arises.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This led me to &lt;a href="https://github.com/tox-dev/tox"&gt;&lt;code&gt;tox&lt;/code&gt;&lt;/a&gt;, an automation tool primarily used for running test suites against multiple version of dependencies. However, it’s also useful for automating all sorts of different tasks, even the ones requiring Python 3 when you’re still using Python 2. To give you a taste of where this leads, the following things were achieved:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The test suite was executed &lt;em&gt;against the installed distribution&lt;/em&gt; and multiple versions of dependencies, thus ensuring the distribution is valid and you support multiple versions of your dependencies. This is achieved by executing &lt;code&gt;tox&lt;/code&gt; in the command line to run the whole test suite, or &lt;code&gt;tox -e &amp;lt;env&amp;gt;&lt;/code&gt; if you want to test a specific use case.&lt;/li&gt;
&lt;li&gt;When one wants to create a new release, one has to execute &lt;code&gt;tox -e release&lt;/code&gt; to create a feature release, or &lt;code&gt;tox -e release -- patch&lt;/code&gt; to create a patch release when &lt;a href="https://semver.org/"&gt;semantic versioning&lt;/a&gt; is used. The release process comprises several other &lt;code&gt;tox&lt;/code&gt; processes, such as &lt;code&gt;changelog&lt;/code&gt; or &lt;code&gt;manifest&lt;/code&gt;, each of which can be executed individually.&lt;/li&gt;
&lt;li&gt;The deployment was executed in the CI environment, but it could also be executed locally. This ensured the distributions are created in a controlled and stable environment, not on someone’s machine with some random settings.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This process was propagated into other packages and into the internal &lt;a href="https://github.com/cookiecutter/cookiecutter"&gt;&lt;code&gt;cookiecutter&lt;/code&gt;&lt;/a&gt; project to make it available to new packages as well. This means that developers had a unified, standardized workflow which enabled everyone to initiate a new release almost instantly, only to have it appear on PyPI in a matter of minutes.&lt;/p&gt;

&lt;p&gt;This post will summarize my experiences and point you to other useful posts covering this subject matter so you don’t waste time on the same issues. I’ll spare you the intricate details of producing the package as there is already enough material on that subject online.&lt;/p&gt;

&lt;p&gt;However, if you need a reference where everything is already confgured, take a look at the &lt;a href="https://github.com/fitodic/centerline"&gt;&lt;code&gt;centerline&lt;/code&gt;&lt;/a&gt; package. I’ve created this package during college as an exercise, but nowadays I use it primarily for testing packaging and distribution. The main files you should be focusing on are &lt;a href="https://github.com/fitodic/centerline/blob/master/setup.cfg"&gt;&lt;code&gt;setup.cfg&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://github.com/fitodic/centerline/blob/master/pyproject.toml"&gt;&lt;code&gt;pyproject.toml&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://github.com/fitodic/centerline/blob/master/.bumpversion.cfg"&gt;&lt;code&gt;.bumpversion.cfg&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://github.com/fitodic/centerline/blob/master/.travis.yml"&gt;&lt;code&gt;.travis.yml&lt;/code&gt;&lt;/a&gt;, and last but not least, &lt;a href="https://github.com/fitodic/centerline/blob/master/tox.ini"&gt;&lt;code&gt;tox.ini&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A bit of history
&lt;/h2&gt;

&lt;p&gt;Python has a rather complicated build and distribution mechanism when it comes to third party libraries. It’s not rocket science, just a large amount of legacy that you have to navigate. The initial idea was to have a &lt;em&gt;batteries included&lt;/em&gt; approach where the Python’s core had all the things you would ever need.&lt;/p&gt;

&lt;p&gt;However, that process inhibited reuse of code that was considered useful, but not useful enough to be included into Python’s core. And so &lt;a href="https://pypi.org"&gt;PyPI&lt;/a&gt; was born, a place where people could upload their Python code and share it with the world. Now you have the best of both worlds, right?&lt;/p&gt;

&lt;p&gt;Well, yes and no. In hindsight, its easy to criticize the decisions that were made at the time that led us to a point where we have &lt;a href="https://discuss.python.org/t/developing-a-single-tool-for-building-developing-projects/2584"&gt;multiple ways to build and distribute libraries&lt;/a&gt; and &lt;a href="https://pyfound.blogspot.com/2019/05/amber-brown-batteries-included-but.html"&gt;call Python’s standard library a dead end&lt;/a&gt;. The point is, although things are not ideal, there is no point complaining about it if you’re not going to do anything about it.&lt;/p&gt;

&lt;p&gt;To correct the course of the entire Python ecosystem, a large amount of time, energy and money is needed. That’s something the open-source community is traditionally scarce at, but &lt;a href="https://discuss.python.org/t/developing-a-single-tool-for-building-developing-projects/2584"&gt;things are improving&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On the other hand, if you are experiencing issues with it, and you want to start somewhere, you can start in your backyard (so to speak). Review the tooling choices that are currently made available to you, standardize your libraries’ maintenance process, obfuscate the internals by providing simple high level APIs or access points to get the job done.&lt;/p&gt;

&lt;p&gt;During this process you’ll familiarize yourself with the ins and outs of the build system, the &lt;a href="https://packaging.python.org/glossary/"&gt;terminology&lt;/a&gt;, and you’ll be able to make informed decisions based on your needs, and spare your colleagues who are less interested in the package distribution.&lt;/p&gt;

&lt;p&gt;With that in mind, I would like to present to you one of the ways that helped us resolve our differences and enabled us to get past the semi-automated phase. It’s important to note that this is not &lt;em&gt;the&lt;/em&gt; way. If it doesn’t suit you or your team’s needs, fine. This also won’t cast blame on the packaging ecosystem, but show you an approach that improved the collaboration on various libraries by adopting the following changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switching to the &lt;code&gt;src/&lt;/code&gt; layout&lt;/li&gt;
&lt;li&gt;Declarative configuration&lt;/li&gt;
&lt;li&gt;Trunk based development&lt;/li&gt;
&lt;li&gt;Changelog and version management&lt;/li&gt;
&lt;li&gt;Release automation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;src&lt;/code&gt; layout
&lt;/h2&gt;

&lt;p&gt;When dealing with Python libraries that are sometimes called packages (i.e. a directory with the &lt;code&gt;__init__.py&lt;/code&gt; file) because they mostly consist of packages that need to be distributed, there are two most-common ways of organizing your codebase:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://packaging.python.org/tutorials/packaging-projects/#creating-the-package-files"&gt;Namespace layout&lt;/a&gt; (I’m not sure it’s the correct term, but let’s go with it for now):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── mypackage
│ ├── __init__.py
│ └── mod1.py
├── tests
├── setup.py
└── setup.cfg

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://setuptools.readthedocs.io/en/latest/setuptools.html#using-a-src-layout"&gt;&lt;code&gt;src/&lt;/code&gt; layouts&lt;/a&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── src
│ └── mypackage
│ ├── __init__.py
│ └── mod1.py
├── tests
├── setup.py
└── setup.cfg

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may be wondering what’s the difference? The difference is how the Python’s &lt;code&gt;import&lt;/code&gt; system treats these files during development and testing, and that has an effect on the build and distribution processes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Development mode
&lt;/h3&gt;

&lt;p&gt;Before we get into details, please bear in mind that some people use symlinks to connect packages to projects where they are being used, whereas others use &lt;a href="https://setuptools.readthedocs.io/en/latest/setuptools.html#development-mode"&gt;development mode&lt;/a&gt; enabled by &lt;a href="https://pip.pypa.io/en/stable/reference/pip_install/#install-editable"&gt;&lt;code&gt;pip&lt;/code&gt;’s &lt;code&gt;editable installs&lt;/code&gt;&lt;/a&gt;. I personally prefer the latter which goes something along the lines of this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkproject centerline # provided by `virtualenvwrapper`
$ git clone https://github.com/fitodic/centerline .
$ pip install -e .[dev,test,docs]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why do I prefer this? Because from my experience, it’s the most stable and reliable development approach when developing and distributing packages. It also drastically simplifies the package’s setup, both in testing and production environments.&lt;/p&gt;

&lt;p&gt;On the other hand, I found &lt;code&gt;symlink&lt;/code&gt;s impractical because not all packages are meant to be used as dependencies in other projects, for example Django packages. And I prefer to have them tested immediately as a standalone codebase. Which brings us to the crux of the problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing packages
&lt;/h3&gt;

&lt;p&gt;When using namespace layouts, i.e. the package is in the project’s root directory where &lt;code&gt;setup.py&lt;/code&gt; is located, Python will implicitly include the current directory in &lt;code&gt;sys.path&lt;/code&gt;. This means that when you run your test suite, the tests will be ran against the code you have in your current working directory, &lt;em&gt;not the installed distribution&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If you don’t think thats a big deal, let me give you an example. Let’s say you have a library that has a test suite that is executed in the CI (Continuous Integration) environment. Someone introduces a change to the library’s configuration (e.g. &lt;code&gt;setup.py&lt;/code&gt;) or forgets to include some static files in the &lt;code&gt;MANIFEST.in&lt;/code&gt; file (they are not picked up by default). All the tests pass and you create a new release.&lt;/p&gt;

&lt;p&gt;The release is successfully deployed to PyPI and installed by your users who start getting &lt;code&gt;ImportError&lt;/code&gt;s. The library is clearly installed, but its empty. It has its metadata so everything looks OK from &lt;code&gt;pip&lt;/code&gt;’s point of view, &lt;strong&gt;but the code is missing&lt;/strong&gt; (or parts of it).&lt;/p&gt;

&lt;p&gt;How can this scenario be avoided? By placing your code in the &lt;code&gt;src/&lt;/code&gt; directory and configuring &lt;code&gt;setuptools&lt;/code&gt; (or whichever build system you are using) to search for packages in the &lt;code&gt;src/&lt;/code&gt; directory. &lt;strong&gt;This forces you to install the distribution that would be shipped to your users and running the test suite against it.&lt;/strong&gt; That way, if there are any configuration errors, you’ll catch them immediately.&lt;/p&gt;

&lt;p&gt;You may have also noticed that in the &lt;code&gt;src/&lt;/code&gt; layout pictured above, the &lt;code&gt;tests&lt;/code&gt; directory is located on the same level as is the &lt;code&gt;src/&lt;/code&gt; directory. The &lt;code&gt;pytest&lt;/code&gt; documentation has a chapter on &lt;a href="https://docs.pytest.org/en/latest/goodpractices.html"&gt;good integration practices&lt;/a&gt; which I recommend you read. The approach described above is in line with these practices for exactly these reasons, to improve the reliability of the entire library development process.&lt;/p&gt;

&lt;p&gt;If you are interested in more details, I highly recommend the following blog posts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.ionelmc.ro/2017/09/25/rehashing-the-src-layout/"&gt;Rehashing the &lt;code&gt;src&lt;/code&gt; layout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.ionelmc.ro/2014/06/25/python-packaging-pitfalls/"&gt;Python packaging pitfalls&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.ionelmc.ro/2014/05/25/python-packaging/"&gt;Packaging a Python library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hynek.me/articles/testing-packaging/#src"&gt;Testing &amp;amp; packaging&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Declarative configuration
&lt;/h2&gt;

&lt;p&gt;To make the package usable, you have to configure it before you deploy it. This configuration comprises package metadata, the list of dependencies that need to be installed, what to include into the distribution, the build system, etc. Unfortunately, at this moment, there are a number of files you have to configure to achieve this.&lt;/p&gt;

&lt;p&gt;Let’s start with the build phase. &lt;a href="https://www.python.org/dev/peps/pep-0518/"&gt;PEP-518&lt;/a&gt; introduced the &lt;a href="https://snarky.ca/clarifying-pep-518/"&gt;&lt;code&gt;pyproject.toml&lt;/code&gt;&lt;/a&gt; which enables users to use either &lt;a href="https://github.com/pypa/setuptools"&gt;&lt;code&gt;setuptools&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://github.com/takluyver/flit"&gt;&lt;code&gt;flit&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://github.com/python-poetry/poetry"&gt;&lt;code&gt;poetry&lt;/code&gt;&lt;/a&gt; to build the distribution from source.&lt;/p&gt;

&lt;p&gt;Why is this important? Long story short, when &lt;code&gt;pip&lt;/code&gt; installs the package from an &lt;a href="https://packaging.python.org/glossary/"&gt;&lt;code&gt;sdist&lt;/code&gt; (source distribution)&lt;/a&gt;, it executes the &lt;code&gt;setup.py&lt;/code&gt; file in order to build a &lt;code&gt;wheel&lt;/code&gt; (binary distribution). When executing &lt;code&gt;setup.py&lt;/code&gt;, it assumes its only dependencies are &lt;code&gt;setuptools&lt;/code&gt; and &lt;code&gt;wheel&lt;/code&gt;, and it needs &lt;code&gt;setuptools&lt;/code&gt; to do that. What if this is deployed in a restrained environment without &lt;code&gt;setuptools&lt;/code&gt; installed beforehand? How do you specify which package to download in order to install your own package? Especially when you need &lt;code&gt;setuptools&lt;/code&gt; to read the configuration in the first place.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pyproject.toml&lt;/code&gt; solves this issue by allowing you to specify the build dependencies, and you only need &lt;code&gt;pip&lt;/code&gt;. You could have specified the build dependencies in &lt;code&gt;setup.py&lt;/code&gt; before that, but then again, you had to have &lt;code&gt;setuptools&lt;/code&gt; installed to read it.&lt;/p&gt;

&lt;p&gt;There is also another thing that is problematic with &lt;code&gt;setup.py&lt;/code&gt;. You could find all manner of customized “stuff” in them, some even requiring dependencies that were yet to be installed. For instance, the &lt;code&gt;setup.py&lt;/code&gt; imported the library’s version from &lt;code&gt;myproject/ __init__.py&lt;/code&gt;, which also contained imports to dependencies. But how did we come to that? I suppose package developers treated packages as regular projects and kept using &lt;code&gt;requirements.txt&lt;/code&gt; files, custom build scripts and various other procedures to “make it work”.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://setuptools.readthedocs.io/en/latest/setuptools.html#configuring-setup-using-setup-cfg-files"&gt;declarative configuration&lt;/a&gt; is a way to limit the scope of abuse. By declaring everything in &lt;code&gt;setup.cfg&lt;/code&gt;, or one day &lt;code&gt;pyproject.toml&lt;/code&gt; if it supports it, there aren’t many ways to “hack” your way around it. I hope.&lt;/p&gt;

&lt;p&gt;Even though I prefer &lt;code&gt;setup.cfg&lt;/code&gt;, that doesn’t mean you cannot put all of this in &lt;code&gt;setup.py&lt;/code&gt; to achieve the same result. It’s a matter of preference, although &lt;a href="https://coverage.readthedocs.io/en/coverage-5.0.3/changes.html#version-5-0b1-2019-11-11"&gt;more&lt;/a&gt; and &lt;a href="https://github.com/psf/black#pyprojecttoml"&gt;more&lt;/a&gt; tools have started adding support for reading their configuration from &lt;code&gt;pyproject.toml&lt;/code&gt;. In my opinion, one file would ideally hold the entire project’s configuration, but then again, it’s a matter of preference.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependencies
&lt;/h3&gt;

&lt;p&gt;Libraries declare, or &lt;em&gt;should&lt;/em&gt; declare, their dependencies in spans to support multiple versions simultaneously, unlike projects like Web applications, where you would want to specify the exact version that’s being deployed. Furthermore, you would also want to list you extra dependencies that users may or may not install, depending on their needs. You can use the same extra dependencies for setting up your development and testing environment as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;install_requires =
    Fiona&amp;gt;=1.7.0
    Shapely&amp;gt;=1.5.13
    numpy&amp;gt;=1.10.4
    scipy&amp;gt;=0.16.1
    Click&amp;gt;=7.0

[options.extras_require]
dev =
    tox
gdal =
    GDAL&amp;gt;=2.3.3
lint =
    flake8
    isort
    black
test =
    pytest&amp;gt;=4.0.0
    pytest-cov
    pytest-sugar
    pytest-runner
docs =
    sphinx

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Trunk based development
&lt;/h2&gt;

&lt;p&gt;Once the library’s configuration is in place, other developers will most likely join in and couldn’t care less about the package’s structure and deployment, as long as it works. But collaboration on a package isn’t just run the test suite -&amp;gt; code review -&amp;gt; merge changes. Packages often provide functionality that builds on its dependencies.&lt;/p&gt;

&lt;p&gt;As with all dependencies, there are deprecation periods and sometimes, its just not possible to continue supporting all versions. The Python 2 to 3 migration is one such example where packages simultaneously supported both version of Python, and then dropped Python 2. These types of changes impact their users the most, so they should be executed with care.&lt;/p&gt;

&lt;p&gt;The difficult part is supporting multiple incompatible versions at the same time. A &lt;code&gt;compat.py&lt;/code&gt; module, or the module where all the compatibility edge-cases are hidden, can only get you so far. At some point in time, you’ll want to create a release that will only continue receiving security patches, while most of the development and innovation carries on.&lt;/p&gt;

&lt;p&gt;This is where release management from the version control system’s perspective kicks in. There are several available options, such as &lt;a href="https://www.gitflow.com/"&gt;Git Flow&lt;/a&gt; or &lt;a href="https://trunkbaseddevelopment.com/"&gt;Trunk based development&lt;/a&gt; (TBD). I’ve used both of them when working on libraries and can safely say that TBD produces more satisfying results.&lt;/p&gt;

&lt;p&gt;In a nutshell, TBD requires developers to create short-lived branches from the &lt;code&gt;master&lt;/code&gt; branch that will be merged back into the &lt;code&gt;master&lt;/code&gt; branch. Every once in a while, a release branch is created from the &lt;code&gt;master&lt;/code&gt; branch that carries a version designation, such as &lt;code&gt;release_1_11&lt;/code&gt;. That branch is the source for creating 1.11.X releases until the version or branch is deprecated. Meanwhile, all the security patches that are merged into the &lt;code&gt;master&lt;/code&gt; branch are &lt;a href="https://git-scm.com/docs/git-cherry-pick"&gt;&lt;code&gt;cherry-pick&lt;/code&gt;ed&lt;/a&gt; into the &lt;code&gt;release&lt;/code&gt; branch. If you’re lucky and the developers working on the project make &lt;a href="https://chris.beams.io/posts/git-commit/"&gt;sensible commits and commit messages&lt;/a&gt;, it’s even easier to manage releases, especially around bugfixes and security patches.&lt;/p&gt;

&lt;p&gt;This approach enables the continuation of feature development according to the project’s roadmap, without worrying too much about backwards compatibility. After all, that’s what the &lt;code&gt;release&lt;/code&gt; branches are for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Changelog and version management
&lt;/h2&gt;

&lt;p&gt;One of the reasons why TBD was adopted in the first place was changelog and version management. My team inherited a customized GitFlow workflow that introduced more frustration when someone introduced an ill-advised method for handling multiple releases simultaneously. There were practically two codebases whose functionality was more or less the same, apart from the code that handled compatibility issues between two versions of a certain dependency.&lt;/p&gt;

&lt;p&gt;This brought even more frustration when a new release had to be made. Various merge requests were issued, slow pipelines executed, merge conflicts resolved (mostly around the changelog), and so forth. There had to be a better way.&lt;/p&gt;

&lt;p&gt;After some research, I stumbled upon TBD and everything just clicked as described in the previous chapter. Furthermore, I dropped the custom built changelog and versioning script in favor of &lt;a href="https://github.com/hawkowl/towncrier"&gt;&lt;code&gt;towncrier&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/peritus/bumpversion"&gt;&lt;code&gt;bumpversion&lt;/code&gt;&lt;/a&gt;. I don’t think there is a need to go into too many details as their documentations are preety straightforward. &lt;a href="https://github.com/tox-dev/tox/issues/1081"&gt;After some minor issues&lt;/a&gt;, these two libraries were successfully integrated into &lt;code&gt;tox&lt;/code&gt; workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Release automation
&lt;/h2&gt;

&lt;p&gt;The last step to set up was the release process. This is also a &lt;code&gt;tox&lt;/code&gt; environment, named &lt;code&gt;release&lt;/code&gt; that built the changelog using &lt;code&gt;towncrier&lt;/code&gt;, bumped the package version using &lt;code&gt;bumpversion&lt;/code&gt;, tagged it and pushed the changes to &lt;code&gt;origin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;From there on, the CI would run the test suite one more time, again using &lt;code&gt;tox&lt;/code&gt; so developers can easily reproduce issues if they arise, build the standard (&lt;code&gt;sdist&lt;/code&gt;) and binary (&lt;code&gt;wheel&lt;/code&gt;) distributions, and upload them to PyPI using &lt;a href="https://github.com/pypa/twine"&gt;&lt;code&gt;twine&lt;/code&gt;&lt;/a&gt;. If you are using Travis CI, you can use &lt;a href="https://docs.travis-ci.com/user/deployment/pypi/"&gt;its own mechanism for uploading distributions&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;As mentioned earlier, this is one approach that may or may not suit you and your team. Each library used here has an alternative and it also comes down to a matter of preference.&lt;/p&gt;

&lt;p&gt;If you are starting out or need to create a new library, I would advise you to use one fo the existing Python cookiecutters, such as &lt;a href="https://github.com/ionelmc/cookiecutter-pylibrary"&gt;&lt;code&gt;cookiecutter-pylibrary&lt;/code&gt;&lt;/a&gt;. You can also build your own as I did. Beats copy/pasting coded from an existing project or documentation.&lt;/p&gt;

</description>
      <category>python</category>
      <category>productivity</category>
      <category>architecture</category>
      <category>django</category>
    </item>
    <item>
      <title>Optimizing websites and APIs with cache and ETags</title>
      <dc:creator>Filip Todic</dc:creator>
      <pubDate>Tue, 12 Nov 2019 16:20:58 +0000</pubDate>
      <link>https://dev.to/fitodic/optimizing-websites-and-apis-with-cache-and-etags-22l3</link>
      <guid>https://dev.to/fitodic/optimizing-websites-and-apis-with-cache-and-etags-22l3</guid>
      <description>&lt;p&gt;When creating a public website or a web app with a public API, you generally want things to run as smooth as possible for the end-user. You also might want to minimize the server’s response time and resource consumption and you want to do it as fast as possible.&lt;/p&gt;

&lt;p&gt;Why would you do that? For any number of reasons, but most often as your site’s traffic increases, it becomes a necessity to speed it up as much as possible.&lt;/p&gt;

&lt;p&gt;How would you do that? Performance benchmarks, when done properly, oftentimes tell you where you should look. At first, most people focus on tuning database queries or revisit some of the tools or libraries they are using. Although these steps are important and have their merits, that’s not why we are here. We are here because you want to get the most bang for your buck, especially if you have a website or API that’s read a lot. That’s where cache and &lt;code&gt;ETag&lt;/code&gt; come in.&lt;/p&gt;

&lt;p&gt;Before we dive right in, I should tell you that when it comes to caching and &lt;code&gt;ETag&lt;/code&gt;s, there is no silver bullet. If someone tells you otherwise, for instance that there is a service or an approach that magically makes all your problems go away, you can safely assume their set of experiences and situations they have dealt with is most probably rather limited. There’s nothing wrong with that, just be aware that their solutions might not be applicable to your use case.&lt;/p&gt;

&lt;p&gt;Why am I telling you this? Because I’ve had this talk with several colleagues and noticed that I was repeating the same story and sending the same references all over again. This is my first attempt at scaling this process. I don’t have all the answers and there are certainly things that I am not aware of at the moment, but I’m going to outline the things I wish someone had told me when I was first starting out in this direction, i.e. optimizing web sites and APIs for high intensity traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cache
&lt;/h2&gt;

&lt;p&gt;Have you noticed how good computers are at repetitive tasks? Well, it turns out even computers have their limits. This limit depends mostly on the amount of “juice” your machine has at its disposal, but ask yourself this: &lt;strong&gt;why would you want to execute the same query/computation/operation, over and over again, for each user, only to get the same result?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When a user makes a request to a specific resource, for instance its profile, What the server or application generally does is it computes the result or retrieves the data from the database, serializes it into the requested format and sends it back to the user. Without cache, this process is repeated for each request, over and over again.&lt;/p&gt;

&lt;p&gt;There has to be a better way, right? Regardless of the amount of users that are triggering this event, why would you want to waste precious resources on something like that? Wouldn’t these resources be better spent on something else? Or not spent at all? Is there a better way?&lt;/p&gt;

&lt;p&gt;As it turns out, there is. Consider the case we’ve described above. If we use a caching mechanism, the server will save the result (retrieved or serialized data) into memory before sending it to the user who has made the request. That way, if the same resource is requested again, instead of retrieving or computing the result again, the end-result will be ready. It will be retrieved from memory and sent back to the user almost instantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Services that provide cache
&lt;/h3&gt;

&lt;p&gt;Sounds great, but you may be asking yourself how can you implement it? If you are using a database, such as PostgreSQL, you are already using it via &lt;a href="https://www.postgresql.org/docs/current/runtime-config-resource.html#GUC-SHARED-BUFFERS"&gt;&lt;code&gt;shared_buffers&lt;/code&gt;&lt;/a&gt;. This setting tells PostgreSQL the amount of memory it has at its disposal for caching data. The defaults are pretty conservative, as are some of its other settings, so you may want to &lt;a href="https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server"&gt;tune it&lt;/a&gt;, depending on your needs and available resources.&lt;/p&gt;

&lt;p&gt;Apart from the database itself, there are services that are dedicated to caching data. &lt;a href="https://www.memcached.org/"&gt;&lt;code&gt;memcached&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://redis.io/"&gt;&lt;code&gt;redis&lt;/code&gt;&lt;/a&gt; are two popular options, but there are many more. They are oftentimes key-value storages where you can store the results of database calls, results of computations or even entire rendered pages. Most programming languages and Web frameworks have libraries and interfaces to interact with them so you don’t have to reinvent the wheel. For example, &lt;a href="https://docs.djangoproject.com/en/dev/topics/cache/"&gt;Django has an entire section dedicated to cache&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTP cache
&lt;/h3&gt;

&lt;p&gt;When working with the Web, it is not enough to cache your own data. There are other forces at work here outside of your control, which are sometimes called &lt;a href="https://docs.djangoproject.com/en/dev/topics/cache/#downstream-caches"&gt;“downstream” caches&lt;/a&gt;, such as Internet Service Providers (ISP), cache proxies or even the Web browsers themselves. These caches may come between the end-user and your application, and it is important to be aware of them, especially when dealing with private data.&lt;/p&gt;

&lt;p&gt;To be on the safe side, you should send HTTP headers that define the resource’s cache policy. The two of most important headers are &lt;a href="https://dev.tocache-control"&gt;&lt;code&gt;Cache-Control&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://dev.tovary"&gt;&lt;code&gt;Vary&lt;/code&gt;&lt;/a&gt;. &lt;a href="https://dev.tocache-control"&gt;&lt;code&gt;Cache-Control&lt;/code&gt;&lt;/a&gt; defines whether or not the response should be cached by the client, for how long and when should it be revalidated, whereas the &lt;code&gt;Vary&lt;/code&gt; header indicates which HTTP headers are used when selecting a representation of the resource. If you are using a Web framework, it probably has its own mechanisms for generating these headers. For example, Django has decorators for &lt;a href="https://docs.djangoproject.com/en/dev/topics/cache/#using-vary-headers"&gt;the &lt;code&gt;Vary&lt;/code&gt; HTTP header&lt;/a&gt;, as well as for the &lt;a href="https://docs.djangoproject.com/en/dev/topics/cache/#controlling-cache-using-other-headers"&gt;&lt;code&gt;Cache-Control&lt;/code&gt; HTTP header&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As a side note, if you are using &lt;code&gt;SessionAuthenticationMiddleware&lt;/code&gt;, &lt;a href="https://code.djangoproject.com/ticket/23939"&gt;each response will have a &lt;code&gt;Vary: Cookie&lt;/code&gt; response header&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache expiration
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;There are only two hard things in Computer Science: cache invalidation and naming things.– Phil Karlton&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since Web frameworks provide most of the mechanisms to deal with cache, both internally (via their APIs and interfaces) and externally (via HTTP headers). That means that the hardest part is left to the developer: &lt;strong&gt;how to invalidate cache so that fresh data is sent as soon as possible?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Theoretically, data can be stored in- and be served from cache indefinitely, but you don’t want that. You want to evict old and rarely used data, but what about data that is updated?&lt;/p&gt;

&lt;p&gt;Once a resource is updated on the server, the previous version of the same resource stored in cache should be either evicted, invalidated or updated in order to server the new, updated version. Which path should you choose? It’s basically up to you, i.e. your application and use-case, but there are several options that come to mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;You can add a &lt;code&gt;last_modified&lt;/code&gt; field to each resource (for simplicity’s sake let’s assume a resource represents a row in a database table) that it updated on each save, and use the resource’s primary key (e.g. its ID) and the &lt;code&gt;last_modified&lt;/code&gt; field to construct a cache key and use it to save to and retrieved the data from cache. This is a relatively simple approach that is widely applicable, however it does have the overhead of retrieving the object from the database, at the very least its ID and &lt;code&gt;last_modified&lt;/code&gt; values to construct the cache key, before retrieving the cached data. Worst case scenario is that if the cache is empty, two database queries will be executed before the data is stored in cache for subsequent requests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The first approach could be further optimized by caching all database queries, using something like &lt;a href="https://django-cachalot.readthedocs.io/en/latest/"&gt;&lt;code&gt;django-cachalot&lt;/code&gt;&lt;/a&gt; which &lt;a href="https://django-cachalot.readthedocs.io/en/latest/how.html#monkey-patching"&gt;caches the results of database queries and clears them once the data in the database table is changed&lt;/a&gt;. However, this approach does have its limitations, &lt;a href="https://django-cachalot.readthedocs.io/en/latest/limits.html#high-rate-of-database-modifications"&gt;primarily when there is a high amount of database modifications&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To avoid the aforementioned issues, you could implement something like &lt;a href="https://docs.djangoproject.com/en/dev/topics/cache/#the-per-site-cache"&gt;&lt;em&gt;per-site&lt;/em&gt;&lt;/a&gt; or &lt;a href="https://docs.djangoproject.com/en/dev/topics/cache/#the-per-view-cache"&gt;&lt;em&gt;per-view&lt;/em&gt;&lt;/a&gt; cache, but a vanilla implementation such as this means your application would be serving stale data until the previous version expires. This could be alleviated by passing a cache key constructor to &lt;code&gt;drf-extensions&lt;/code&gt;’ &lt;a href="https://chibisov.github.io/drf-extensions/docs/#cache-key"&gt;&lt;code&gt;cache_response&lt;/code&gt; decorator&lt;/a&gt;, but that oftentimes raises the issues mentioned in point 1.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you want fine-grained control, you can always &lt;a href="https://docs.djangoproject.com/en/dev/topics/cache/#template-fragment-caching"&gt;cache response fragments&lt;/a&gt;, but that is susceptible to nitpicking and inadvertently caching block that should not be cached (e.g. user-specific data).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There is also &lt;a href="https://docs.djangoproject.com/en/dev/topics/cache/#cache-versioning"&gt;cache versioning&lt;/a&gt;. Basically, each time a resource is updated, the cache key’s version is incremented. Although this means that only the latest version of the resource is cached, the downside is that you should keep tabs on version numbers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, you can always use the &lt;a href="https://docs.djangoproject.com/en/dev/topics/cache/#the-low-level-cache-api"&gt;low-level API&lt;/a&gt;, i.e. implementing the cache invalidation logic yourself. For example, each time a resource is saved, you would construct the resource’s cache key, try to locate it, and if it doesn’t exist, create it. If it does, delete it and set it anew.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As I said before, the path you choose to go is entirely up to you. There is nothing wrong with combining all of the aforementioned approaches. As always, the most important thing to do is to choose the right tools for the job. These are just pointers with useful links so you know what are your options are and where to start. The end result depends primarily on your application and usage patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  ETag
&lt;/h2&gt;

&lt;p&gt;In the HTTP cache section I mentioned “downstream” caches and the role that &lt;code&gt;Cache-Control&lt;/code&gt; and &lt;code&gt;Vary&lt;/code&gt; headers play at informing proxy caches and other intermediaries about the resource’s &lt;em&gt;freshness&lt;/em&gt;. As it turns out, there is another set of HTTP headers that helps clients (e.g. Web browsers) determine whether or not the received resource is still fresh, and those headers are &lt;a href="https://dev.toetag"&gt;&lt;code&gt;ETag&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://dev.toif-match"&gt;&lt;code&gt;If-Match&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://dev.toif-none-match"&gt;&lt;code&gt;If-None-Match&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://dev.toetag"&gt;&lt;code&gt;ETag&lt;/code&gt;&lt;/a&gt; HTTP response header is as a resource identifier, or better yet, an identifier of a particular version of the resource. Sort of like the resource’s fingerprint that changes whenever the resource is updated. This identifier usually comes in the form of a hash, generated by the server. The algorithm that’s used to generate it primarily depends on the resource it is calculating the &lt;code&gt;ETag&lt;/code&gt; for, but there are a few common use-cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the resource is an entry in the database, you can use the database table’s name (including the schema if necessary), the entry’s primary key and the &lt;code&gt;last_modified&lt;/code&gt; field (if available);&lt;/li&gt;
&lt;li&gt;If the resource is an HTML page composed of various different resources, you can use the entire HTML representation as input;&lt;/li&gt;
&lt;li&gt;If the resource is a file on disk, the file’s modification time and size can be used as inputs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may be asking yourself how does this help the cache process? Why go through all this trouble of generating the &lt;code&gt;ETag&lt;/code&gt; just to match it with the resource it came with? There are two reasons you would want to do that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;It tells the client that sent the request (e.g. Web browser) to cache the response and keep reusing it until the &lt;code&gt;ETag&lt;/code&gt; changes, thus saving bandwidth.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It prevents the client from updating the resource if the resource has been updated in the meantime.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How is this achievable? By sending conditional requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conditional requests
&lt;/h3&gt;

&lt;p&gt;Sending either &lt;a href="https://dev.toif-match"&gt;&lt;code&gt;If-Match&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://dev.toif-none-match"&gt;&lt;code&gt;If-None-Match&lt;/code&gt;&lt;/a&gt; header in the request makes the request conditional, but when should you use which? The good news is that browsers do most of it for you by default, but here are the details behind these types of requests.&lt;/p&gt;

&lt;p&gt;When retrieving data, the Web browser requests the resource from the server and receives the resource’s representation along with a couple of HTTP headers, among which is the &lt;code&gt;ETag&lt;/code&gt; header. The &lt;code&gt;ETag&lt;/code&gt;’s value is used for retrieving the cached resource from the browser’s cache. After a while, the resource needs to be revalidated (depending on the expiration time set by the &lt;code&gt;Cache-Control&lt;/code&gt; HTTP header), so the browser requests the resource from the server (again), but this time, it adds the &lt;code&gt;ETag&lt;/code&gt;’s value in the &lt;code&gt;If-None-Match&lt;/code&gt; HTTP header. This tells the server to check whether or not the resource has changed since the last request. The server generates the resource’s &lt;code&gt;ETag&lt;/code&gt; and if the two values match, the resource has not changed, and the server returns a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304"&gt;&lt;code&gt;304 Not Modified&lt;/code&gt;&lt;/a&gt; response with an empty body, telling the browser to use the cached resource. Otherwise, the server returns a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200"&gt;&lt;code&gt;200 OK&lt;/code&gt;&lt;/a&gt; response with the resource’s new representation and a new &lt;code&gt;ETag&lt;/code&gt; value. &lt;strong&gt;What’s the benefit of this process? You save on bandwidth by not receiving the data you already have stored locally.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That covers data retrieval, but the &lt;code&gt;ETag&lt;/code&gt; header is also useful when updating resources. In fact, it helps prevent simultaneous updates of the same resource from overwriting each other, or &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag#Caching_of_unchanged_resources"&gt;“mid-air collisions”&lt;/a&gt;. How would you do that? Let’s say you’ve retrieved a resource and edited it in your browser. You click the &lt;em&gt;Save&lt;/em&gt; button, and the browser sends a PUT request with &lt;code&gt;ETag&lt;/code&gt;’s value in the &lt;code&gt;If-Match&lt;/code&gt; HTTP header. If the resource has changed in the meantime, the server will respond with a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/412"&gt;&lt;code&gt;412 Precondition Failed&lt;/code&gt;&lt;/a&gt; response, thus &lt;strong&gt;preventing you from overriding the resource that has been updated while you were editing it.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Caching is a broad subject and this post is intended to give you a high-level overview of its application in Web development. This post includes a couple of examples, but there are certainly many more, from code snippets to HTTP headers that can be used as well (e.g. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified"&gt;&lt;code&gt;Last-Modified&lt;/code&gt;&lt;/a&gt;). If you want to get into more details, there are references to other resources that go into more practical details, especially from the &lt;a href="https://developer.mozilla.org/en-US/"&gt;Mozilla Developer Network&lt;/a&gt; and the &lt;a href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt; Web framework, both of them being excellent sources for further reading.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching"&gt;MDN: HTTP cache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.djangoproject.com/en/dev/topics/cache/"&gt;Django: Cache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;PostgreSQL: &lt;a href="https://www.postgresql.org/docs/"&gt;Docs&lt;/a&gt; and &lt;a href="https://wiki.postgresql.org/wiki/Main_Page"&gt;Wiki&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>performance</category>
      <category>architecture</category>
      <category>django</category>
    </item>
  </channel>
</rss>
