<?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: David Rios</title>
    <description>The latest articles on DEV Community by David Rios (@davidrios).</description>
    <link>https://dev.to/davidrios</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%2F609771%2F31686671-1563-4d78-afc7-3c3ce31fad0d.jpeg</url>
      <title>DEV Community: David Rios</title>
      <link>https://dev.to/davidrios</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/davidrios"/>
    <language>en</language>
    <item>
      <title>Localizing a real world Vue.js app [part 1]</title>
      <dc:creator>David Rios</dc:creator>
      <pubDate>Tue, 13 Apr 2021 23:04:53 +0000</pubDate>
      <link>https://dev.to/davidrios/localizing-a-real-world-vue-js-app-part-1-2084</link>
      <guid>https://dev.to/davidrios/localizing-a-real-world-vue-js-app-part-1-2084</guid>
      <description>&lt;p&gt;In this series I'll demonstrate one way of localizing a real world Vue.js app using these excellent Mozilla projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://projectfluent.org/"&gt;Fluent&lt;/a&gt;: "Fluent is a family of localization specifications, implementations and good practices developed by Mozilla. With Fluent, translators can create expressive translations that sound great in their language."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://pontoon.mozilla.org"&gt;Pontoon&lt;/a&gt;: "Pontoon is a translation management system used and developed by the Mozilla localization community. It specializes in open source localization that is driven by the community and uses version-control systems for storing translations."&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My aim is to establish a workflow that scales well with an application's growing size and number of locales and translators. This series will chronicle my journey to realize that goal.&lt;/p&gt;

&lt;p&gt;For this first part I'll focus on adapting the app code. The second part will focus on using &lt;code&gt;Pontoon&lt;/code&gt; to improve the collaboration process with the team of translators.&lt;/p&gt;

&lt;p&gt;The idea is to have one or more catalogs based on your app structure. At a minimum we'll have a base catalog, we'll call it &lt;code&gt;global&lt;/code&gt;, that has the basic text needed for the app to work initially, possibly being the only catalog if your app is small. You can then create other catalogs with varying levels of granularity. If your app is big and you use dynamic components to load only the part the user is in, you could for instance have a &lt;code&gt;profile&lt;/code&gt; catalog that would be loaded for the profile or any other related page. At the finest granularity we can even have component-specific catalogs.&lt;/p&gt;

&lt;p&gt;The catalog need to be loaded in the code at some point, and for that there are some options, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A wrapper component that handle the loading, possibly displaying some loading indication.&lt;/li&gt;
&lt;li&gt;Manually using a functional API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'll focus on using a functional API for the sake of simplicity.&lt;/p&gt;

&lt;p&gt;I want the catalogs to be able to be treated as part of a whole, in which case the keys are global and each catalog adds its keys to the global pool. It would be nice if they could also be bound to a context, inheriting or not from the global pool, in which case overwritten keys would only affect components under that context.&lt;/p&gt;

&lt;h2&gt;
  
  
  The app
&lt;/h2&gt;

&lt;p&gt;I want to demonstrate the process of localizing an existing app, and it would be nice to use a real world application as an example. For that we'll use the &lt;a href="https://github.com/gothinkster/vue-realworld-example-app"&gt;Vue RealWorld example app&lt;/a&gt; as a starting point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting up
&lt;/h2&gt;

&lt;p&gt;To make it easier for you to follow, I've set up a GitHub repository with all the code at &lt;a href="https://github.com/davidrios/vue-realworld-example-app"&gt;https://github.com/davidrios/vue-realworld-example-app&lt;/a&gt;. I'll refer to specific commits so you can see the changes along the way.&lt;/p&gt;

&lt;p&gt;This is the state of the code with minor changes made after forking:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/vue-realworld-example-app/tree/f621d819"&gt;https://github.com/davidrios/vue-realworld-example-app/tree/f621d819&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll use the excellent &lt;a href="https://fluent-vue.demivan.me"&gt;fluent-vue&lt;/a&gt; project, that already implements fluent for Vue.js. First install the packages:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add fluent-vue @fluent/bundle intl-pluralrules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We're using Vue 2 in this example so, per fluent-vue requirement, we need to also install:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add @vue/composition-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Loading the locale files
&lt;/h2&gt;

&lt;p&gt;We'll start simple and use the &lt;code&gt;raw-loader&lt;/code&gt; to easily load ftl files through webpack by adding this configuration:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/vue-realworld-example-app/commit/e5038262"&gt;https://github.com/davidrios/vue-realworld-example-app/commit/e5038262&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we need to load the catalogs. It would be nice if we chose the catalog based on the user's language as detected by the browser. For that I added some code to detect the language, load catalogs and setup &lt;code&gt;fluent-vue&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/vue-realworld-example-app/commit/cff8b43f"&gt;https://github.com/davidrios/vue-realworld-example-app/commit/cff8b43f&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This code will be improved on later.&lt;/p&gt;

&lt;p&gt;From &lt;code&gt;015c35dc&lt;/code&gt; to &lt;code&gt;307bf3ca&lt;/code&gt; I just extracted strings for translation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/vue-realworld-example-app/compare/015c35dc...307bf3ca"&gt;https://github.com/davidrios/vue-realworld-example-app/compare/015c35dc...307bf3ca&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here I have improved the catalog loading and added the option for the user to change the locale at runtime:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/vue-realworld-example-app/commit/0585b5a1"&gt;https://github.com/davidrios/vue-realworld-example-app/commit/0585b5a1&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Live reload of locale files
&lt;/h3&gt;

&lt;p&gt;As I was making more translations, I started to dislike the fact that the whole page was reloaded every time I changed any catalog, which I think is unecessary. I know webpack has a way to reload only the parts that changed with the right configuration, so I searched around but could not find anything that suited my needs.&lt;/p&gt;

&lt;p&gt;I ended up writing my own loader to help with that:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# https://www.npmjs.com/package/@davidrios/hot-reloader
yarn add -D @davidrios/hot-reloader
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And then I refactored all the catalog loading code to be more generic and make use of webpack's HMR, so now changed catalogs update the page instantly without a reload:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/vue-realworld-example-app/commit/fbc238ee"&gt;https://github.com/davidrios/vue-realworld-example-app/commit/fbc238ee&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Separating catalogs
&lt;/h2&gt;

&lt;p&gt;Separating the app in more than one catalog will be quite easy thanks to the last update to the loading code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/vue-realworld-example-app/commit/45c2ea72"&gt;https://github.com/davidrios/vue-realworld-example-app/commit/45c2ea72&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Some localization examples
&lt;/h2&gt;

&lt;p&gt;Using fluent's own date formatting:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/vue-realworld-example-app/commit/ccc4da77"&gt;https://github.com/davidrios/vue-realworld-example-app/commit/ccc4da77&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Localizing content with tags
&lt;/h3&gt;

&lt;p&gt;One very common problem of localizing web apps arise when you need HTML tags / components in the middle of some text. Consider the example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;'x'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sign up&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt; or &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;'y'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;sign in&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt; to add comments on this article.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or even worse, using components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;router-link&lt;/span&gt; &lt;span class="na"&gt;:to=&lt;/span&gt;&lt;span class="s"&gt;"{ name: 'login' }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sign in&lt;span class="nt"&gt;&amp;lt;/router-link&amp;gt;&lt;/span&gt;
  or
  &lt;span class="nt"&gt;&amp;lt;router-link&lt;/span&gt; &lt;span class="na"&gt;:to=&lt;/span&gt;&lt;span class="s"&gt;"{ name: 'register' }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;sign up&lt;span class="nt"&gt;&amp;lt;/router-link&amp;gt;&lt;/span&gt;
  to add comments on this article.
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Best practices of localization (actually the only sensible thing to do!), say that you should translate the sentence as a whole, so how do we do that without risking translators messing up the code or worse, introducing security issues? Luckly &lt;code&gt;vue&lt;/code&gt; is powerful enough to provide the tools necessary to tackle that problem, and the &lt;code&gt;fluent-vue&lt;/code&gt; project do a perfect job of realizing that with the help of &lt;code&gt;fluent&lt;/code&gt;'s powerful syntax.&lt;/p&gt;

&lt;p&gt;The fluent code would look 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;# The two parameters will be replaced with links and each link
# will use the .sign-*-label as its text
sign-in-up-to-add-comments =
  {$signInLink} or {$signUpLink} to add comments on this article.
  .sign-in-label = Sign in
  .sign-up-label = sign up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I personally think the result is great. We have comments explaining what is happening, it's very flexible to the translator, the pieces needed are in-context and there's no HTML in sight!&lt;/p&gt;

&lt;p&gt;For the vue part, &lt;code&gt;fluent-vue&lt;/code&gt; provides a nice component named &lt;code&gt;i18n&lt;/code&gt; with everything we need. The vue code would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;i18n&lt;/span&gt; &lt;span class="na"&gt;path=&lt;/span&gt;&lt;span class="s"&gt;"sign-in-up-to-add-comments"&lt;/span&gt; &lt;span class="na"&gt;tag=&lt;/span&gt;&lt;span class="s"&gt;"p"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="na"&gt;#signInLink=&lt;/span&gt;&lt;span class="s"&gt;"{ signInLabel }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;router-link&lt;/span&gt; &lt;span class="na"&gt;:to=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;{ name: 'login' }"&amp;gt;&lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;signInLabel&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/router-link&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="na"&gt;#signUpLink=&lt;/span&gt;&lt;span class="s"&gt;"{ signUpLabel }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;router-link&lt;/span&gt; &lt;span class="na"&gt;:to=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;{ name: 'register' }"&amp;gt;&lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;signUpLabel&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/router-link&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/i18n&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;path&lt;/code&gt; property takes the name of the translation key.&lt;/li&gt;
&lt;li&gt;Each variable in the text, like &lt;code&gt;$signInLink&lt;/code&gt; can be used either as a direct value by passing it as args to the &lt;code&gt;i18n&lt;/code&gt; component, for instance &lt;code&gt;:args="{ signInLink: 'The link' }"&lt;/code&gt;, or like in the previous example as a &lt;a href="https://vuejs.org/v2/guide/components-slots.html#Named-Slots"&gt;named slot&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Each named slot receives the other translation attributes as slot props with their keys camelized. In the previous example they would receive the object: &lt;code&gt;{ signInLabel: 'Sign in', signUpLabel: 'sign up' }&lt;/code&gt;, so you can use object destructuring to make the code cleaner, like &lt;code&gt;#signInLink="{ signInLabel }"&lt;/code&gt;, which will receive the value of the translation attribute &lt;code&gt;.sign-in-label&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fluent syntax is very powerful, yet relatively simple, I highly recommend you take the time to read the full guide &lt;a href="https://projectfluent.org/fluent/guide/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Managing fluent catalogs
&lt;/h1&gt;

&lt;p&gt;The idea is to manage the localization files using &lt;a href="https://github.com/mozilla/pontoon"&gt;Pontoon&lt;/a&gt; but, since that will be discussed later in part 2 of this series, for the sake of completeness in this article I added a simple script that updates a catalog based on the base locale one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/vue-realworld-example-app/commit/1a8f7767"&gt;https://github.com/davidrios/vue-realworld-example-app/commit/1a8f7767&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks to the good folks at the &lt;code&gt;fluent&lt;/code&gt; project that provided an API for dealing with catalogs programatically with the subproject &lt;a href="https://projectfluent.org/fluent.js/syntax/"&gt;@fluent/syntax&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can run the script executing:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn update-catalog LOCALE CATALOG_NAME [FROM_LOCALE]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;FROM_LOCALE&lt;/code&gt; is an optional parameter that if not provided will default to 'en-US'. To update the &lt;code&gt;pt-BR&lt;/code&gt; &lt;code&gt;global&lt;/code&gt; catalog for instance you would execute:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn update-catalog pt-BR global
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will merge the contents of the &lt;code&gt;FROM_LOCALE&lt;/code&gt; catalog with the chosen one, preserving comments from both and moving keys that doens't exit in the base catalog to the end of the file with a comment noting that. The resulting file will be saved with a new name if it already exists or with the final name if creating a new one.&lt;/p&gt;

&lt;p&gt;I've used the script to merge the catalogs, translated the rest of the keys and published a build here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://davidrios.github.io/vue-realworld-example-app/"&gt;https://davidrios.github.io/vue-realworld-example-app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that's all for now. With all this in place I hope you already have the basic framework to start localizing your apps the "right way", while being convenient for the developers and easy on the translators.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>l10n</category>
      <category>i18n</category>
      <category>vue</category>
      <category>fluent</category>
    </item>
    <item>
      <title>Como configurar um ambiente de desenvolvimento com Docker + VS Code</title>
      <dc:creator>David Rios</dc:creator>
      <pubDate>Wed, 07 Apr 2021 04:24:08 +0000</pubDate>
      <link>https://dev.to/davidrios/como-configurar-um-ambiente-de-desenvolvimento-com-docker-vs-code-2pc8</link>
      <guid>https://dev.to/davidrios/como-configurar-um-ambiente-de-desenvolvimento-com-docker-vs-code-2pc8</guid>
      <description>&lt;p&gt;There's a version in english here:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/davidrios" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--J0sbCa1---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--yeGDHWTq--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/609771/31686671-1563-4d78-afc7-3c3ce31fad0d.jpeg" alt="davidrios image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/davidrios/how-to-configure-a-docker-vs-code-development-environment-3jko" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;How to configure a Docker + VS Code development environment&lt;/h2&gt;
      &lt;h3&gt;David Rios ・ Apr  7 ・ 13 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#vscode&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#docker&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Já tem um tempo que venho trabalhando com Docker em alguns projetos, e fui fazendo várias configurações até que cheguei num modelo que funciona bem para mim e minha equipe, por isso decidi compartilhá-lo, caso seja útil pra mais pessoas.&lt;/p&gt;

&lt;p&gt;Tenha em mente que é tudo baseado na minha experiência pessoal, podendo não usar as melhores práticas ou se aplicar ao seu caso. Mas aceito sugestões de melhorias mesmo assim!&lt;/p&gt;

&lt;p&gt;Vou te mostrar como configurar um projeto inteiro do zero e rodar ele totalmente isolado dentro de contêineres, para que o ambiente seja reproduzível e funcione da mesma forma (eu espero), não importa o seu SO.&lt;/p&gt;

&lt;p&gt;Eu uso Ubuntu no dia-a-dia, então este guia é dessa perspectiva, mas você deve conseguir adaptar para seu SO de preferência sem muita dificuldade. Provavelmente deve funcionar bem no Windows 10 com WSL 2, mas ainda não testei para ter certeza.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tabela de Tópicos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;O Projeto&lt;/li&gt;
&lt;li&gt;Requisitos&lt;/li&gt;
&lt;li&gt;Começando&lt;/li&gt;
&lt;li&gt;A aplicação &lt;em&gt;backend&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;A aplicação &lt;em&gt;frontend&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Um &lt;em&gt;proxy&lt;/em&gt; nginx simples&lt;/li&gt;
&lt;li&gt;
VS Code para o alto e avante!

&lt;ul&gt;
&lt;li&gt;Configurando para o &lt;em&gt;backend&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Configurando para o &lt;em&gt;frontend&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Adendo&lt;/li&gt;
&lt;li&gt;Conclusão&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  O Projeto &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Para ilustrar todos os passos, e ficar o mais realista possível, vamos criar um aplicativo web básico usando a seguinte &lt;em&gt;stack&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend: &lt;a href="https://www.djangoproject.com/"&gt;Python (Django)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Database: &lt;a href="https://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Frontend: &lt;a href="https://quasar.dev/"&gt;VueJS (Quasar)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ao final do guia você terá um ambiente de edição de código consistente com:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Code-completion&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Linting&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Debugging&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Configurações bem definidas de edição de código para todos trabalhando no projeto.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O motivo de escolha dessa &lt;em&gt;stack&lt;/em&gt; é por serem tecnologias que eu já estou familiarizado, mas a maioria dos conceitos deste guia devem se aplicar a qualquer outra que você escolher.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requisitos &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Vamos precisar de poucas coisas instaladas localmente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/get-docker/"&gt;Docker&lt;/a&gt; 19+ (uma versão recente)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/compose/install/"&gt;docker-compose&lt;/a&gt; v1.27+&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://code.visualstudio.com/"&gt;VS Code&lt;/a&gt; 1.5+&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;E estas extensões para o VS Code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers"&gt;Remote - Containers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Começando &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Para ficar mais fácil de você seguir, subi um repositório no GitHub com todo o código em &lt;a href="https://github.com/davidrios/example-docker-project"&gt;https://github.com/davidrios/example-docker-project&lt;/a&gt;. Vou ligar cada passo ao &lt;em&gt;commit&lt;/em&gt; relevante conforme formos prosseguindo, assim você poderá ver toda a estrutura conforme ela evolui.&lt;/p&gt;

&lt;p&gt;Estamos começando o projeto do zero, então a primeiro coisa que precisamos organizar é sua estrutura. Esta é uma das que eu gosto de usar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Raíz do projeto

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;README&lt;/em&gt; e coisa do docker&lt;/li&gt;
&lt;li&gt;conf

&lt;ul&gt;
&lt;li&gt;projeto 1&lt;/li&gt;
&lt;li&gt;projeto 2&lt;/li&gt;
&lt;li&gt;misc&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;code

&lt;ul&gt;
&lt;li&gt;projeto 1&lt;/li&gt;
&lt;li&gt;projeto 2&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A próxima coisa que precisamos é de alguns contêineres básicos rodando, para podermos inicializar os projetos. Pela descrição do projeto, já sabemos que precisamos de pelo menos três contêineres: &lt;em&gt;backend&lt;/em&gt;, &lt;em&gt;frontend&lt;/em&gt; e &lt;em&gt;database&lt;/em&gt;. Para isso precisamos criar o arquivo &lt;code&gt;docker-compose.yml&lt;/code&gt; base. Mas antes, uma das coisas mais úteis que podemos ter é um arquivo &lt;code&gt;.env&lt;/code&gt;, para que possamos ter algum nível de personalização sobre os contêineres.&lt;/p&gt;

&lt;p&gt;Criemos o arquivo &lt;code&gt;.env.template&lt;/code&gt; inicial, que cada usuário fará uma cópia local própria para personalizar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LOCAL_USER_ID=1000
POSTGRES_PASSWORD=password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A variável &lt;code&gt;LOCAL_USER_ID&lt;/code&gt; é usada para lidar com um problema de permissões que pode ocorrer com o Docker no Linux (mais sobre isso mais tarde). Você define ela com o &lt;em&gt;uid&lt;/em&gt; do seu usuário no seu computador local, que pode ser consultado executando &lt;code&gt;id -u&lt;/code&gt; no terminal. Você pode ignorar esse passo se estiver no Mac.&lt;/p&gt;

&lt;p&gt;Depois de criar nossa cópia local do &lt;code&gt;.env&lt;/code&gt;, é hora de definir o arquivo &lt;code&gt;docker-compose.yml&lt;/code&gt; básico:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/blob/2aac6eb151104b205461d025ca07647c44bc5d36/docker-compose.yml"&gt;https://github.com/davidrios/example-docker-project/blob/2aac6eb151104b205461d025ca07647c44bc5d36/docker-compose.yml&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pontos de atenção:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Eu importo o arquivo &lt;code&gt;.env&lt;/code&gt; nas linhas 3 a 5, e passo todas as variáveis definidas nele como variáveis de ambiente para todos os outros contêineres. Você pode ver como isso é feito nas linhas 10 e 11, 19 e 20, etc.&lt;/li&gt;
&lt;li&gt;Estou usando imagens Alpine com versões &lt;em&gt;major&lt;/em&gt; e &lt;em&gt;minor&lt;/em&gt; especifícadas, como mandam as boas práticas. Você pode querer ou ser obrigado a usar uma imagem baseada em Ubuntu/Debian/etc, mas deve sempre escolher versões &lt;em&gt;major&lt;/em&gt;/&lt;em&gt;minor&lt;/em&gt; específicas.&lt;/li&gt;
&lt;li&gt;Foi definido um volume para os dados do PostgreSQL, para que sejam preservados entre cada execução/reconstrução.&lt;/li&gt;
&lt;li&gt;Sempre que possível prefiro rodar todos os projetos usando um usuário comum (não administrador) dentro dos contêineres, e por gosto de criar volumes para a &lt;em&gt;home&lt;/em&gt; desse usuário para cada contêiner que eu espero logar e executar códigos com frequência, assim posso ter algumas coisas bacanas como o histórico do &lt;em&gt;shell&lt;/em&gt; e outras coisas preservadas entre os &lt;em&gt;rebuilds&lt;/em&gt;. Isso é uma preferência pessoal e completamente opcional, você pode simplesmente remover os volumes. No entanto, apesar disso ser bem conveniente, tenha em mente que pode ter consequências sutis na reproducibilidade geral do ambiente.&lt;/li&gt;
&lt;li&gt;Para o &lt;em&gt;backend&lt;/em&gt; e o &lt;em&gt;frontend&lt;/em&gt;, como eles ainda não têm uma aplicação configurada, mas preciso deles rodando de qualquer forma para poder inicializar as aplicações, inicialmente eu coloco eles para rodar somente um longo &lt;code&gt;sleep&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neste momento podemos navegar para a raíz do projeto e executar:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker-compose up --build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Você deve ver algumas mensagens do PostgreSQL sobre a inicialização do banco de dados e os três contêineres devem estar rodando.&lt;/p&gt;

&lt;h2&gt;
  
  
  A aplicação &lt;em&gt;backend&lt;/em&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Eu gosto de executar a aplicação com um usuário comum, e para prevenir problemas de permissão no Linux, esse usuário precisa ter o mesmo &lt;em&gt;uid&lt;/em&gt; que meu usuário local do computador. Esta é a razão para a variável &lt;code&gt;LOCAL_USER_ID&lt;/code&gt; no arquivo &lt;code&gt;.env&lt;/code&gt;. Para poder seguir com este plano, vamos precisar de alguns &lt;em&gt;scripts&lt;/em&gt; auxiliares, que serão usados dentro dos contêineres:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;base.sh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;run.sh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;enter.sh&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vamos adicionálos a um diretório chamado &lt;code&gt;conf/backend/scripts&lt;/code&gt;, como convencionado anteriormente. Esses &lt;em&gt;scripts&lt;/em&gt; serão executados somente no ambiente de desenvolvimento, então não farão parte da imagem Docker.&lt;/p&gt;

&lt;p&gt;Também vamos criar um arquivo &lt;code&gt;Dockerfile&lt;/code&gt; para o contêiner com algumas personalizações iniciais. O arquivo do docker-compose será atualizado com a nova imagem e pontos de montagem para o código e os &lt;em&gt;scripts&lt;/em&gt;. Um diretório chamado &lt;code&gt;code&lt;/code&gt; também será criado, e por fim o docker-compose será executado novamente:&lt;/p&gt;

&lt;p&gt;Veja como o projeto mudou:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/commit/04ae1e95ca8dfe752e76a67fce5b9882847f2f8e"&gt;https://github.com/davidrios/example-docker-project/commit/04ae1e95ca8dfe752e76a67fce5b9882847f2f8e&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="c"&gt;## *** paramos o docker-compose ***&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;code
&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora temos o contêiner rodando e pronto para criar o projeto Django. Usamos o &lt;em&gt;script&lt;/em&gt; &lt;code&gt;enter.sh&lt;/code&gt; para entrar no contêiner com o &lt;code&gt;venv&lt;/code&gt; já ativado e logado com o usuário correto. Agora vamos criar o projeto:&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="c"&gt;## Em outro terminal, na raíz do projeto:&lt;/span&gt;

&lt;span class="c"&gt;## Cria o banco de dados:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; postgres postgres-db createdb &lt;span class="nt"&gt;-T&lt;/span&gt; template0 &lt;span class="nt"&gt;-E&lt;/span&gt; utf8 backend

&lt;span class="c"&gt;## Entra no contêiner do *backend*:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose &lt;span class="nb"&gt;exec &lt;/span&gt;backend /scripts/enter.sh

&lt;span class="c"&gt;## Agora dentro do contêiner:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /code
&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-binary&lt;/span&gt; psycopg2 django psycopg2
&lt;span class="nv"&gt;$ &lt;/span&gt;django-admin startproject backend
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;backend

&lt;span class="c"&gt;## Salvamos o *requirements*:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;pip freeze &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;## *** Editamos o arquivo backend/settings.py para conectar à instância do PostgreSQL pegando a senha do ambiente e os dados de conexão já definidos ***&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;python manage.py migrate
&lt;span class="nv"&gt;$ &lt;/span&gt;python manage.py createsuperuser
&lt;span class="c"&gt;## *** escolhe os dados do usuário para login no admin ***&lt;/span&gt;

&lt;span class="c"&gt;## Testa que a aplicação roda:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;python manage.py runserver

&lt;span class="c"&gt;## *** Parece tudo certo, paramos o servidor e saímos do contêiner ***&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dê uma olhada nos arquivos adicionados, com atenção especial à configuração do banco de dados:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/commit/8f47c701e6a3984a32021da360ae0c49a2d68a95#diff-192a1d9e9543969133c5449ace7b1169de815b39d539bc55fc1d168f32eedb7bR76-R85"&gt;https://github.com/davidrios/example-docker-project/commit/8f47c701e6a3984a32021da360ae0c49a2d68a95#diff-192a1d9e9543969133c5449ace7b1169de815b39d539bc55fc1d168f32eedb7bR76-R85&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora precisamos mandar a aplicação executar automaticamente junto com o contêiner e expor a porta para que possamos acessar em nosso navegador local. A instalação dos pacotes necessários também será adicionada na imagem.&lt;/p&gt;

&lt;p&gt;Para podermos executar localmente em qualquer porta de nossa escolha e não entrar em conflito com nenhuma outra coisa rodando, vamos adicionar esta opção como uma nova linha no arquivo &lt;code&gt;.env.template&lt;/code&gt; e a mesma na nossa cópia &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;APP_PORT=8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;além de fazer as alterações relacionadas ao docker. Veja as mudanças:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/commit/c9276155e56f1f8c0168bb81902e5e0f22ed0dad"&gt;https://github.com/davidrios/example-docker-project/commit/c9276155e56f1f8c0168bb81902e5e0f22ed0dad&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora paramos o docker-compose e executamos novamente, sempre com o parâmetro &lt;code&gt;--buid&lt;/code&gt;. A aplicação deve estar rodando e acessível no endereço &lt;a href="http://localhost:8000"&gt;http://localhost:8000&lt;/a&gt; (ou outra porta caso você tenha modificado)&lt;/p&gt;

&lt;p&gt;É isso por enquanto para o &lt;em&gt;backend&lt;/em&gt;. A configuração do VS Code vem mais abaixo no guia.&lt;/p&gt;

&lt;h2&gt;
  
  
  A aplicação &lt;em&gt;frontend&lt;/em&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Este é bem similar ao &lt;em&gt;backend&lt;/em&gt;. Vamos criar os mesmos três &lt;em&gt;scripts&lt;/em&gt; auxiliares, com algumas mudanças específicas para este ambiente, e personalizar a imagem um pouco.&lt;/p&gt;

&lt;p&gt;Vamos aproveitar este momento e adicionar o arquivo &lt;code&gt;.dockerignore&lt;/code&gt;, para que o Docker não tenha que copiar um monte de arquivos desnecessários na hora que for fazer o &lt;em&gt;build&lt;/em&gt; de qualquer imagem. Isso vai agilizar o processo consideravelmente, a depender do tamanho do seu projeto.&lt;/p&gt;

&lt;p&gt;Veja as mudanças:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/commit/bbb2c6180437c8e1ae5b3b0ca2121e78dc1250af"&gt;https://github.com/davidrios/example-docker-project/commit/bbb2c6180437c8e1ae5b3b0ca2121e78dc1250af&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="c"&gt;## *** Paramos o docker-compose e iniciamos novamente ***&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vamos inicializar o projeto Quasar da mesma forma:&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;docker-compose &lt;span class="nb"&gt;exec &lt;/span&gt;frontend /scripts/enter.sh
&lt;span class="nv"&gt;$ &lt;/span&gt;yarn global add @quasar/cli
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /code
&lt;span class="nv"&gt;$ &lt;/span&gt;~/.yarn/bin/quasar create frontend
&lt;span class="c"&gt;## *** escolhendo opções do quasar ***&lt;/span&gt;
&lt;span class="c"&gt;## Escolha pelo menos o ESLint, pois será usado depois.&lt;/span&gt;
&lt;span class="c"&gt;## Também vou escolher o TypeScript, só para demonstrar o suporte do VS Code&lt;/span&gt;
&lt;span class="c"&gt;## E finalmente escolha um *preset* ESLint que você vai usar no seu projeto. Agora vou ficar com o "standard"&lt;/span&gt;
&lt;span class="c"&gt;## Deixe o Quasar instalar os pacotes com o yarn e saia do contêiner&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora vamos configurar o contêiner do &lt;em&gt;frontend&lt;/em&gt; para iniciar o Quasar em modo de desenvolvimento automaticamente e exportar a porta para que possamos acessá-lo.&lt;/p&gt;

&lt;p&gt;Eis o resultado das mudanças:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/commit/73e3b55df164ea2e1477cb51a8d591299c5f4643"&gt;https://github.com/davidrios/example-docker-project/commit/73e3b55df164ea2e1477cb51a8d591299c5f4643&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note que só movemos a porta exportada do contêiner do &lt;em&gt;backend&lt;/em&gt; para o do &lt;em&gt;frontend&lt;/em&gt;, mas já vamos consertar isso.&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="c"&gt;## *** Paramos o docker-compose e iniciamos novamente ***&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora o aplicativo Quasar deve estar rodando e acessível no endereço &lt;a href="http://localhost:8000"&gt;http://localhost:8000&lt;/a&gt; (ou a porta que você definiu).&lt;/p&gt;

&lt;h2&gt;
  
  
  Um &lt;em&gt;proxy&lt;/em&gt; nginx simples &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Para facilitar nossa vida, vamos criar um &lt;em&gt;proxy&lt;/em&gt; simples mas flexível com o nginx na frente de tudo. Vamos precisar usar um arquivo personalizado, e para uma maior flexibilidade vamos processar esse arquivo e misturar ele com as variáveis de ambiente antes de iniciar o serviço.&lt;/p&gt;

&lt;p&gt;Estas são as mudanças feitas:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/commit/b07c83199fd8d4b1cd4dfce1e830a19e76b64543"&gt;https://github.com/davidrios/example-docker-project/commit/b07c83199fd8d4b1cd4dfce1e830a19e76b64543&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora podemos acessar a aplicação &lt;em&gt;frontend&lt;/em&gt; no endereço &lt;a href="http://localhost:8000"&gt;http://localhost:8000&lt;/a&gt;, o admin Django em &lt;a href="http://localhost:8000/admin"&gt;http://localhost:8000/admin&lt;/a&gt;, além do nginx redirecionar tudo vindo de &lt;code&gt;/api*&lt;/code&gt; para a aplicação &lt;em&gt;backend&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  VS Code para o alto e avante! &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Agora que temos todo nosso projeto organizadinho e rodando isolado do nosso ambiente local, precisamos começar a codar. Não seria ótimo se pudéssemos ter a mesma experiência agradável e consistente dos contêineres no nosso editor de texto também? É aí que entra o VS Code!&lt;/p&gt;

&lt;p&gt;Vamos usar a extensão "Remote - Containers" para executar o VS Code diretamente dentro dos contêineres das aplicações, então ele vai ter acesso ao ambiente exatamente como ele roda, e também podemos instalar extensões sem afetar nossa instalação local.&lt;/p&gt;

&lt;p&gt;Vamos usar um diretório chamado &lt;code&gt;code/vscode&lt;/code&gt; para colocar as coisas relacionadas, para que também já estejam disponíveis dentro dos contêineres graças ao ponto de montagem &lt;code&gt;/code&lt;/code&gt;. Você poderia colocar em outra pasta, mas aí também teria que configurar um outro ponto de montagem para cada contêiner.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configurando para o &lt;em&gt;backend&lt;/em&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Primeiro criamos o diretório &lt;code&gt;code/vscode/backend&lt;/code&gt; e adicionamos os seguintes arquivos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;backend.code-workspace&lt;/code&gt;: Substitua "backend" pelo nome real do seu projeto, assim fica mais fácil navegar entre as janelas abertas do Code.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;flake8.ini&lt;/code&gt;: Onde colocamos nossas configurações de &lt;em&gt;linter&lt;/em&gt; para o Python. Vamos usar flake8 ao invés do pylint, porque ele é muito mais rápido e também porque eu gosto mais :)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.devcontainer.json&lt;/code&gt;: Diz ao Code como configurar sua instância no contêiner&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Também será necessário editar o &lt;em&gt;script&lt;/em&gt; &lt;code&gt;base.sh&lt;/code&gt; do contêiner, assim ele instala os pacotes necessário mesmo entre os &lt;em&gt;rebuilds&lt;/em&gt;, além de iniciar a aplicação Python em modo de depuração. Como um agradinho também vamos configurar o servidor para recarregar automaticamente cada vez que algum arquivo é mudado, para isso utilizando a ótima biblioteca &lt;a href="https://pypi.org/project/watchdog/"&gt;Watchdog&lt;/a&gt;. Isso também é necessário pois a extensão de depuração do Code não suporta o recarregamento automático nativo do Django. De brinde o watchdog ainda usa bem menos CPU para monitorar as mudanças em arquivos que o sistema nativo do Django.&lt;/p&gt;

&lt;p&gt;Dá uma olhada como ficou (já adicionei também uma API de teste):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/commit/33cb5d775b88d8b953ba690c17e1359dd5fa34a5"&gt;https://github.com/davidrios/example-docker-project/commit/33cb5d775b88d8b953ba690c17e1359dd5fa34a5&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora tudo que precisamos fazer é parar o compose e rodar novamente, então tudo estará rodando e pronto. Para trabalhar com o código nós abrimos uma nova janela do Code (&lt;code&gt;Ctrl+Shift+N&lt;/code&gt;), vamos no menu &lt;code&gt;File &amp;gt; Open Folder&lt;/code&gt; e abrimos a pasta &lt;code&gt;&amp;lt;PROJECT&amp;gt;/code/vscode/backend&lt;/code&gt;. No mesmo momento já deve aparecer uma notificação, então é só clicar no botão &lt;code&gt;Reopen in Container&lt;/code&gt;. Também é possível utilizar a paleta de comandos (&lt;code&gt;Ctrl+Shift+P&lt;/code&gt;), começar a digitar &lt;code&gt;reopen in container&lt;/code&gt; e escolher a opção &lt;code&gt;Remote-Containers: Reopen in container&lt;/code&gt;. Logo mais ele vai abrir, mostrar uma notificação de que está instalando o Code server e extensões, aí após alguns instantes o código deve ser aberto. No final terá uma indicação na barra de &lt;em&gt;status&lt;/em&gt; de que você entá dentro de um contêiner, desta forma:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dqGnb42w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d8a9mwayw50e7f8leqo1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dqGnb42w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d8a9mwayw50e7f8leqo1.png" alt="" title="Project opened"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Então você terá todas as funcionalidades do editor, assim:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8o4xA3bU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rnma2gluz7tsmkg6a6p9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8o4xA3bU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rnma2gluz7tsmkg6a6p9.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;E para depurar, é só clicar na margem ao lado do número da linha no editor para configurar um ponto de pausa, ou pressionar a tecla &lt;code&gt;F9&lt;/code&gt; com a linha desejada selecionada, assim:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jPtMIIF7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6iy71kxn27d8838r74h2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jPtMIIF7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6iy71kxn27d8838r74h2.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Então clique no menu lateral para ir à janela &lt;code&gt;Run&lt;/code&gt; e clique no botão verde com o símbolo de &lt;em&gt;play&lt;/em&gt;. A barra de &lt;em&gt;status&lt;/em&gt; deve mudar para a cor laranja, indicando que o depurador está conectado. Se você tiver um ponto de pausa, como eu tinha, e navegar para a API no navegador, você será contemplado com esta maravilha:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S6HEyZxQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fppt8xkui449cavm18xx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S6HEyZxQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fppt8xkui449cavm18xx.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cada vez que um arquivo mudar, o servidor fará o recarregamento automático, desta forma:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VqsShtZD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/66kmx40xrhsnkhiyyx18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VqsShtZD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/66kmx40xrhsnkhiyyx18.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;E se você precisar executar algum comando administrativo do Django, pode facilmente com o terminal integrado. É só abrir o menu &lt;code&gt;Terminal &amp;gt; New Terminal&lt;/code&gt; (&lt;code&gt;Ctrl+Shift+&amp;lt;BACKTICK&amp;gt;&lt;/code&gt;), desse jeito:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3EC5LMW6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/apy8i292493id3w7x254.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3EC5LMW6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/apy8i292493id3w7x254.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Se por acaso o terminal não já estiver com o "venv" ativado, é só clicar no botão de &lt;code&gt;lixeira&lt;/code&gt; para matar o terminal e abrí-lo novamente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configurando para o &lt;em&gt;frontend&lt;/em&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Aqui a configuração é bem similar à feita acima. Primeiro criamos o diretório &lt;code&gt;code/vscode/frontend&lt;/code&gt; e adicionamos o arquivo &lt;code&gt;.devcontainer.json&lt;/code&gt;. Aqui não vou configurar um arquivo de projeto pois não tem mais nenhuma pasta além da principal do projeto, e também para aproveitar os arquivos de configuração que o Quasar já cria na inicialização do projeto. Caso desejasse, você poderia mover tudo para um arquivo de projeto também, como fizemos no caso do &lt;em&gt;backend&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Aqui está o resultado das mudanças:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/commit/644b7547322d903f7e4b33454b787e5d1d4c6f49"&gt;https://github.com/davidrios/example-docker-project/commit/644b7547322d903f7e4b33454b787e5d1d4c6f49&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Desta vez nem precisa parar e rodar o docker-compose, só abrir uma nova janela do Code, abrir o diretório &lt;code&gt;code/vscode/frontend&lt;/code&gt; e clicar no botão &lt;code&gt;Reload in Container&lt;/code&gt;. Depois de abrir o projeto, clique no botão &lt;code&gt;ESLint&lt;/code&gt; na barra de &lt;em&gt;status&lt;/em&gt; e escolha a opção &lt;code&gt;allow&lt;/code&gt; para permitir a execução dele. Agora você tem um editor configurado, desta maneira:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n1XRS9Cu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hu4cin7flkrlbmaimg74.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n1XRS9Cu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hu4cin7flkrlbmaimg74.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Você tem até sugestões de completação para coisas como os componentes do Quasar!&lt;/p&gt;

&lt;p&gt;Se quiser usar o &lt;em&gt;CLI&lt;/em&gt; do Quasar para ações como criar um componente por exemplo, é só abrir o terminal embutido clicando no menu em &lt;code&gt;Terminal &amp;gt; New Terminal&lt;/code&gt; (&lt;code&gt;Ctrl+Shift+&amp;lt;BACKTICK&amp;gt;&lt;/code&gt;), assim:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9LJYaoEh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1r3aj6tl1zm31y8bddd7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9LJYaoEh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1r3aj6tl1zm31y8bddd7.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora é hora de depurar!&lt;/p&gt;

&lt;p&gt;Para isso, vamos criar um arquivo &lt;code&gt;launch.json&lt;/code&gt; com as tarefas de depuração do Code. Isto não é tão trivial por algumas razões:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O VS Code tem dois depuradores que funcionam com o Chrome. Um novo que já vem pré instalado e o antigo que você pode baixar do &lt;em&gt;marketplace&lt;/em&gt;. O problema é o que o novo é melhor, mas por alguma razão, suspeito eu que tenha a ver com o fato de rodar remotamente, ele não consegue lançar instâncias do Chrome. O antigo consegue, mas ele não é tão bom. Para resolver esse problema usaremos ambos!&lt;/li&gt;
&lt;li&gt;Como temos uma porta personalizável através do nosso arquivo &lt;code&gt;.env&lt;/code&gt;, precisamos de algum jeito de passá-la para a tarefa de depuração. Infelizmente as extensões de depuração executam no editor local, não no remoto, então elas não têm acesso às variáveis de ambiente. Para resolver esse problema, adicionei uma caixa de diálogo que solicita a porta que a aplicação está rodando, com o mesmo valor padrão do arquivo &lt;em&gt;template&lt;/em&gt; do &lt;code&gt;.env&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O arquivo de tarefas de depuração:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/blob/6e5195fd13d2aea79b1a31c265ddf60808d9a77e/code/frontend/.vscode/launch.json"&gt;https://github.com/davidrios/example-docker-project/blob/6e5195fd13d2aea79b1a31c265ddf60808d9a77e/code/frontend/.vscode/launch.json&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Temos três tarefas que usaremos dependendo do nosso navegador:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Attach to Chrome&lt;/code&gt;: Esta tarefa utiliza o novo depurador para se apegar à uma instância do Chrome já em execução, desde que tenha depuração remota habilitada, e você não precisa instalar mais nada. &lt;a href="https://blog.chromium.org/2011/05/remote-debugging-with-chrome-developer.html"&gt;Veja aqui como executar o Chrome com a opção de depuração remota habilitada&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Launch Chrome&lt;/code&gt;: Esta lança uma instância do Chrome com a depuração remota já habilitada, e também utilizando um perfil novo, para que não afete o seu pessoal. Para usá-la você precisa &lt;a href="https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome"&gt;instalar a extensão Debugger for Chrome&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Launch Firefox&lt;/code&gt;: E por fim está lança uma instância (ou se apega à uma que já está em execução) do Firefox com a depuração remota habilitada, e também com um perfil novo para não afetar sua instância pessoal. Para utilizar esta tarefa você precisa &lt;a href="https://marketplace.visualstudio.com/items?itemName=firefox-devtools.vscode-firefox-debug"&gt;instalar a extensão Debugger for Firefox&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tem mais uma coisa que podemos fazer para deixar nossa vida mais agradável enquanto depuramos o código. A configuração padrão do &lt;a href="https://webpack.js.org/"&gt;Webpack&lt;/a&gt; não gera &lt;em&gt;sourcemaps&lt;/em&gt; detalhados, o que acaba deixando o trabalho desnecessariamente mais difícil, além de deixar basicamente impossível de configurar pontos de pausa em arquivos que têm um processamento prévio. Nós corrigimos isso adicionando a opção &lt;code&gt;devtool: 'eval-source-map'&lt;/code&gt; nas configurações.&lt;/p&gt;

&lt;p&gt;Mesmo assim, para alguns arquivos processados como os do VueJS, ele gera várias variantes do mesmo arquivo com nomes diferentes para cada resultado intermediário do processamento. Isso é meio irritante, então acabamos com esse problema personalizando a função &lt;code&gt;output.devtoolModuleFilenameTemplate&lt;/code&gt; do Webpack.&lt;/p&gt;

&lt;p&gt;Já que este projeto é em Quasar, vamos fazer do modo correto, como instruído na documentação dele, editando o arquivo &lt;code&gt;quasar.conf.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;No fim das contas ele fica assim:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/commit/1208e30bd280179df0780add22430af3a54c6c30"&gt;https://github.com/davidrios/example-docker-project/commit/1208e30bd280179df0780add22430af3a54c6c30&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora você pode executar o depurador indo na tela de &lt;code&gt;Run&lt;/code&gt;, selecionando a tarefa e clicando no botão de "play":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2BTfJBPE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3n67820af4t7c7npmz0x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2BTfJBPE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3n67820af4t7c7npmz0x.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tente adicionar alguns &lt;code&gt;console.log&lt;/code&gt;s e/ou configurar algum ponto de pausa, rodar no modo de depuração e usar a aplicação. Eventualmente você deve ver algo parecido com isto:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3SFFMCDm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sfbquglbieoj4m8bmdt3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3SFFMCDm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sfbquglbieoj4m8bmdt3.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adendo &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Não sou muito fã de fazer muita coisa dentro do editor de texto, especialmente coias que já podem ser feitas de forma muito mais flexível pelo terminal, por isso que eu não uso a extensão Docker do Code para gerenciar ou executar o docker-compose. Também acho que funciona melhor assim, nesses casos em que você tem um projeto com vários contêineres com aplicações distintas, como no caso deste guia.&lt;/p&gt;

&lt;p&gt;Como estamos gerenciando o fluxo manualmente, algumas vezes o VS Code vai exibir uma notificação destas:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--00h7z7Tl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l5wl8yn6nafoon3m7vo6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--00h7z7Tl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l5wl8yn6nafoon3m7vo6.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Recomendo apenas clicar em &lt;code&gt;Ignore&lt;/code&gt;. Se você precisar fazer o &lt;em&gt;build&lt;/em&gt; novamente, é só parar o compose executando no terminal e rodar um &lt;code&gt;docker-compose up --build&lt;/code&gt; de novo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Têm vários pontos que podem ser melhorados e várias escolhar diferente que você pode fazer. Estas são as que eu fiz e que funcionam bem para mim, não necessariamente são as "certas".&lt;/p&gt;

&lt;p&gt;Note que esta configuração toda é bem mais adequada a times pequenos ou indivíduos, já que times maiores têm outros requisitos, por vezes mutualmente exclusivos com algumas das decisões aqui tomadas.&lt;/p&gt;

&lt;p&gt;Além disso, todo este guia é bem voltado ao desenvolvedor no trabalho diário, então não existem imagens reais que poderiam ser geradas para colocar a aplicação em produção, mas isso pode ser resolvido com um pouco mais de trabalho.&lt;/p&gt;

&lt;p&gt;Obrigado por ler!&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>docker</category>
      <category>webdev</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>How to configure a Docker + VS Code development environment</title>
      <dc:creator>David Rios</dc:creator>
      <pubDate>Wed, 07 Apr 2021 04:04:41 +0000</pubDate>
      <link>https://dev.to/davidrios/how-to-configure-a-docker-vs-code-development-environment-3jko</link>
      <guid>https://dev.to/davidrios/how-to-configure-a-docker-vs-code-development-environment-3jko</guid>
      <description>&lt;p&gt;Há uma versão em português brasileiro aqui:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/davidrios" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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%2Fuser%2Fprofile_image%2F609771%2F31686671-1563-4d78-afc7-3c3ce31fad0d.jpeg" alt="davidrios"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/davidrios/como-configurar-um-ambiente-de-desenvolvimento-com-docker-vs-code-2pc8" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Como configurar um ambiente de desenvolvimento com Docker + VS Code&lt;/h2&gt;
      &lt;h3&gt;David Rios ・ Apr 7 '21&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#vscode&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#docker&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#braziliandevs&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;After working with Docker for a while on some projects, I arrived at a configuration that works well for me and my colleagues, so I decided to share it in case it's useful to more people.&lt;/p&gt;

&lt;p&gt;Keep in mind it's based on my personal experience, it might not use the best practices or apply to your case. Suggestions on improvements are welcome though!&lt;/p&gt;

&lt;p&gt;I'll show you how to setup a full project from scratch and run it completely isolated within containers, so the environment will (hopefully) be reproducible and run the same, no matter your host OS.&lt;/p&gt;

&lt;p&gt;I use Ubuntu as my daily driver, so this guide will be from that perspective, but you should be able to adapt it to your OS of choice without much trouble. It'll probably work well in Windows using WSL 2.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The project&lt;/li&gt;
&lt;li&gt;Requirements&lt;/li&gt;
&lt;li&gt;Starting out&lt;/li&gt;
&lt;li&gt;The backend app&lt;/li&gt;
&lt;li&gt;The frontend app&lt;/li&gt;
&lt;li&gt;A simple nginx proxy&lt;/li&gt;
&lt;li&gt;
VS Code for the win

&lt;ul&gt;
&lt;li&gt;Configuring for the backend&lt;/li&gt;
&lt;li&gt;Configuring for the frontend&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Addendum&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  The project &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;To illustrate all steps, and be as realistic as possible, we'll create a basic web application using the following stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend: &lt;a href="https://www.djangoproject.com/" rel="noopener noreferrer"&gt;Python (Django)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Database: &lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;PostgreSQL&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Frontend: &lt;a href="https://quasar.dev/" rel="noopener noreferrer"&gt;VueJS (Quasar)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the end we'll have a consistent environment for code editing with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code-completion&lt;/li&gt;
&lt;li&gt;Linting&lt;/li&gt;
&lt;li&gt;Debugging&lt;/li&gt;
&lt;li&gt;Well defined editor settings for everyone working on the project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The stack choice is based on what I have more familiarity, but all the concepts in this guide should apply to any stack you choose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;We need very few things installed locally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; 19+ (a recent version)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/compose/install/" rel="noopener noreferrer"&gt;docker-compose&lt;/a&gt; v1.27+&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;VS Code&lt;/a&gt; 1.5+&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And these Code extensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers" rel="noopener noreferrer"&gt;Remote - Containers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Starting out &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;To make it easier for you to follow, I've set up a GitHub repository with all the code at &lt;a href="https://github.com/davidrios/example-docker-project" rel="noopener noreferrer"&gt;https://github.com/davidrios/example-docker-project&lt;/a&gt;. I'll refer to specific commits so you can see the structure along the way.&lt;/p&gt;

&lt;p&gt;We are starting the project from scratch, so the first thing we need is to sort out its structure. This is the one I like to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project root

&lt;ul&gt;
&lt;li&gt;some docker stuff and README&lt;/li&gt;
&lt;li&gt;conf

&lt;ul&gt;
&lt;li&gt;project 1&lt;/li&gt;
&lt;li&gt;project 2&lt;/li&gt;
&lt;li&gt;misc&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;code

&lt;ul&gt;
&lt;li&gt;project 1&lt;/li&gt;
&lt;li&gt;project 2&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The next thing we need is to have some basic containers running to bootstrap the projects. We already know we need at least 3 containers from the project description: backend, frontend and database, so we need to create a base &lt;code&gt;docker-compose.yml&lt;/code&gt;. But before that, one of the most useful things is to have a &lt;code&gt;.env&lt;/code&gt; file, so you can have some amount of customization over the containers.&lt;/p&gt;

&lt;p&gt;Let's create the initial &lt;code&gt;.env.template&lt;/code&gt; file, that each user will make a local copy to customize:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LOCAL_USER_ID=1000
POSTGRES_PASSWORD=password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;LOCAL_USER_ID&lt;/code&gt; is used to work around Docker permission issues on Linux (more on that later). You set it with the uid of your local computer user as returned by the command &lt;code&gt;id -u&lt;/code&gt;. You can just ignore it if you are on Mac.&lt;/p&gt;

&lt;p&gt;After creating my local &lt;code&gt;.env&lt;/code&gt; copy, it's time to define the basic &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;br&gt;
&lt;a href="https://github.com/davidrios/example-docker-project/blob/2aac6eb151104b205461d025ca07647c44bc5d36/docker-compose.yml" rel="noopener noreferrer"&gt;https://github.com/davidrios/example-docker-project/blob/2aac6eb151104b205461d025ca07647c44bc5d36/docker-compose.yml&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Things of note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I import the &lt;code&gt;.env&lt;/code&gt; file at lines 3-5, and pass all defined variables as environment vars to all the other
containers. You can see that at lines 10-11, 19-20, etc.&lt;/li&gt;
&lt;li&gt;I'm using Alpine images with major and minor versions specified, as per best practices. You may want or
need to use an Ubuntu/Debian/etc based image, but you should aways choose specific major/minor versions.&lt;/li&gt;
&lt;li&gt;There is a volume defined for the PostgreSQL data, so that is preserved between runs.&lt;/li&gt;
&lt;li&gt;I run all projects I can as a standard (non root) user in the containers, and I like to create volumes
for the home of that user on every container that I expect to log in and execute commands, that way I
can have the shell history and other stuff preserved between rebuilds. That is an optional and personal
preference, you can just remove those volumes. Be aware that, while using those volumes is convenient,
they may have subtle effects on the overall reproducibility of the environment.&lt;/li&gt;
&lt;li&gt;For the backend and frontend, since they don't have any application configured yet, but I need them running
anyway to be able to bootstrap the apps, I just tell them to run a very long &lt;code&gt;sleep&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this time we can navigate to the main project repository and just run:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker-compose up --build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;You should see some messages from PostgreSQL about the initialization of the database and the three containers should keep running.&lt;/p&gt;
&lt;h2&gt;
  
  
  The backend app &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I like to execute the app as a regular user, and to avoid issues of permissions on Linux, that user need to have the same uid as my local computer user, that's the reason for the &lt;code&gt;LOCAL_USER_ID&lt;/code&gt; in the &lt;code&gt;.env&lt;/code&gt; file. To do that we'll add some helper scripts to be used inside the container:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;base.sh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;run.sh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;enter.sh&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll add them to a directory &lt;code&gt;conf/backend/scripts&lt;/code&gt;, as decided earlier. These scripts will only be executed on the dev environment, so they'll not be a part of the image.&lt;/p&gt;

&lt;p&gt;We'll also create a &lt;code&gt;Dockerfile&lt;/code&gt; for the container with some needed initial customizations. The compose file will be updated with the new image and mounts for the code and scripts. And the &lt;code&gt;code&lt;/code&gt; directory will also be created and docker-compose will be executed again:&lt;/p&gt;

&lt;p&gt;Check how the project has changed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/commit/04ae1e95ca8dfe752e76a67fce5b9882847f2f8e" rel="noopener noreferrer"&gt;https://github.com/davidrios/example-docker-project/commit/04ae1e95ca8dfe752e76a67fce5b9882847f2f8e&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="c"&gt;## *** stop docker-compose ***&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;code
&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have the container running and ready to create the Django project. We use the &lt;code&gt;enter.sh&lt;/code&gt; script to enter the container with the &lt;code&gt;venv&lt;/code&gt; already activated and logged with the correct user. Now let's create the project:&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="c"&gt;## in another terminal at the root of the project folder:&lt;/span&gt;

&lt;span class="c"&gt;## Create the database:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; postgres postgres-db createdb &lt;span class="nt"&gt;-T&lt;/span&gt; template0 &lt;span class="nt"&gt;-E&lt;/span&gt; utf8 backend

&lt;span class="c"&gt;## Enter the backend container:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose &lt;span class="nb"&gt;exec &lt;/span&gt;backend /scripts/enter.sh

&lt;span class="c"&gt;## Now inside the container:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /code
&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-binary&lt;/span&gt; psycopg2 django psycopg2
&lt;span class="nv"&gt;$ &lt;/span&gt;django-admin startproject backend
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;backend

&lt;span class="c"&gt;## Save the requirements:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;pip freeze &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;## *** Edit backend/settings.py to connect to the PostgreSQL db using the password from the environment and the host/dbname/user we already defined ***&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;python manage.py migrate
&lt;span class="nv"&gt;$ &lt;/span&gt;python manage.py createsuperuser
&lt;span class="c"&gt;## *** insert desired login information ***&lt;/span&gt;

&lt;span class="c"&gt;## Test the application runs:&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;python manage.py runserver

&lt;span class="c"&gt;## *** Everything seems ok, stop the server and exit the container ***&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the files added, with special attention to the database configuration:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/commit/8f47c701e6a3984a32021da360ae0c49a2d68a95#diff-192a1d9e9543969133c5449ace7b1169de815b39d539bc55fc1d168f32eedb7bR76-R85" rel="noopener noreferrer"&gt;https://github.com/davidrios/example-docker-project/commit/8f47c701e6a3984a32021da360ae0c49a2d68a95#diff-192a1d9e9543969133c5449ace7b1169de815b39d539bc55fc1d168f32eedb7bR76-R85&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we need to set the application to run automatically with the container and to expose the port so we can access with our local browser. The installation of app packages will also be added to the image.&lt;/p&gt;

&lt;p&gt;To be able to run locally on any port we choose and not conflict with other running stuff, we'll add that option to the &lt;code&gt;.env.template&lt;/code&gt; file and the same line to our &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;APP_PORT=8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and update the docker stuff. Check the diff:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/commit/c9276155e56f1f8c0168bb81902e5e0f22ed0dad" rel="noopener noreferrer"&gt;https://github.com/davidrios/example-docker-project/commit/c9276155e56f1f8c0168bb81902e5e0f22ed0dad&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now stop docker-compose and run it again, always with &lt;code&gt;--build&lt;/code&gt;. The application should now be running and accessible at &lt;a href="http://localhost:8000" rel="noopener noreferrer"&gt;http://localhost:8000&lt;/a&gt; (or other port if you changed it).&lt;/p&gt;

&lt;p&gt;This is it for now for the backend. VS Code configuration instructions will follow later in the guide.&lt;/p&gt;

&lt;h2&gt;
  
  
  The frontend app &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This one is pretty similar to the backend. We'll create the same three helper scripts, with specific changes for the new environment, and customize the image a bit.&lt;/p&gt;

&lt;p&gt;At this time we'll also add a &lt;code&gt;.dockerignore&lt;/code&gt; file, so that Docker doesn't have to copy useless stuff everytime it has to build an image. That'll speed up the process a lot if you already have a big project.&lt;/p&gt;

&lt;p&gt;Take a look at the changes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/commit/bbb2c6180437c8e1ae5b3b0ca2121e78dc1250af" rel="noopener noreferrer"&gt;https://github.com/davidrios/example-docker-project/commit/bbb2c6180437c8e1ae5b3b0ca2121e78dc1250af&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="c"&gt;## *** We stop docker-compose and start it again ***&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We bootstrap the Quasar project the same way:&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;docker-compose &lt;span class="nb"&gt;exec &lt;/span&gt;frontend /scripts/enter.sh
&lt;span class="nv"&gt;$ &lt;/span&gt;yarn global add @quasar/cli
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /code
&lt;span class="nv"&gt;$ &lt;/span&gt;~/.yarn/bin/quasar create frontend
&lt;span class="c"&gt;## *** choose quasar options ***&lt;/span&gt;
&lt;span class="c"&gt;## Pick at least ESLint because it's used later.&lt;/span&gt;
&lt;span class="c"&gt;## I'll also pick TypeScript to demonstrate the great VS Code support later&lt;/span&gt;
&lt;span class="c"&gt;## And finally choose an ESLint preset you wanna use on your project. I'll stick with standard here&lt;/span&gt;
&lt;span class="c"&gt;## Let Quasar install the packages with yarn and get out of the container&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we'll configure the frontend container to start Quasar in dev mode automatically and export the port so that we can access it.&lt;/p&gt;

&lt;p&gt;Here's the resulting changes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/commit/73e3b55df164ea2e1477cb51a8d591299c5f4643" rel="noopener noreferrer"&gt;https://github.com/davidrios/example-docker-project/commit/73e3b55df164ea2e1477cb51a8d591299c5f4643&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that we just moved the export port from the backend to the frontend container, we'll fix that in a bit.&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="c"&gt;## *** We stop docker-compose and start it again ***&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Quasar app should now be running and accessible at &lt;a href="http://localhost:8000" rel="noopener noreferrer"&gt;http://localhost:8000&lt;/a&gt; (or other port if you changed it).&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple nginx proxy &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;To make our lives easier, we'll create a simple but flexible nginx proxy in front of everything. We need to use a custom config file, and for maximum flexibility we'll process the config and interpolate with the env vars before starting the server.&lt;/p&gt;

&lt;p&gt;These are the resulting changes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/commit/b07c83199fd8d4b1cd4dfce1e830a19e76b64543" rel="noopener noreferrer"&gt;https://github.com/davidrios/example-docker-project/commit/b07c83199fd8d4b1cd4dfce1e830a19e76b64543&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can now access the frontend app at &lt;a href="http://localhost:8000" rel="noopener noreferrer"&gt;http://localhost:8000&lt;/a&gt;, the Django admin at &lt;a href="http://localhost:8000/admin" rel="noopener noreferrer"&gt;http://localhost:8000/admin&lt;/a&gt; and nginx will also proxy all requests to &lt;code&gt;/api*&lt;/code&gt; to the backend app.&lt;/p&gt;

&lt;h2&gt;
  
  
  VS Code for the win &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Now that we have our whole project well organized and running isolated from our local machine, we need to start coding it. Wouldn't it be awesome if we could have the same consistent and pleasant experience while coding as well? That's when VS Code comes to the rescue!&lt;/p&gt;

&lt;p&gt;We'll use the remote containers extension to run VS Code directly inside the app containers, so it'll have access to the environment exactly as it runs and we can install extensions without affecting our local installation.&lt;/p&gt;

&lt;p&gt;We will be using a directory named &lt;code&gt;code/vscode&lt;/code&gt; to put related stuff, just so it is already available inside the containers because of the &lt;code&gt;/code&lt;/code&gt; mount. You could put it in another directory, but then you would have to setup another mount for each container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring for the backend &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;First create the directory &lt;code&gt;code/vscode/backend&lt;/code&gt; and add the following files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;backend.code-workspace&lt;/code&gt;: Replace backend with the real nome of your project, so it's easier to pick the Code windows apart when navigating.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;flake8.ini&lt;/code&gt;: Where we put the linter settings for Python. We'll be using flake8 instead of pylint, since it is much faster and I like it better :).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.devcontainer.json&lt;/code&gt;: Tells Code how to setup the container instance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's also necessary to edit the container &lt;code&gt;base.sh&lt;/code&gt; script, so it installs necessary packages after any rebuild and also to start Python in debug mode. As a nice touch we'll also configure the server to autoreload when any file changes using &lt;a href="https://pypi.org/project/watchdog/" rel="noopener noreferrer"&gt;Watchdog&lt;/a&gt;, since the Code debugger extension for Python doesn't suport Django's autoreload. As a bonus, watchdog uses much less CPU than Django's default autoreloader.&lt;/p&gt;

&lt;p&gt;I've gone ahead and added our first test API.&lt;/p&gt;

&lt;p&gt;Check it out:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/commit/33cb5d775b88d8b953ba690c17e1359dd5fa34a5" rel="noopener noreferrer"&gt;https://github.com/davidrios/example-docker-project/commit/33cb5d775b88d8b953ba690c17e1359dd5fa34a5&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now all we have to do is stop compose and run it again, all should be running and ready. To start working with the code we open a new Code window (&lt;code&gt;Ctrl+Shift+N&lt;/code&gt;) and go to &lt;code&gt;File &amp;gt; Open Folder&lt;/code&gt; and open the &lt;code&gt;&amp;lt;PROJECT&amp;gt;/code/vscode/backend&lt;/code&gt; directory. Right away a notification should appear, you just click the &lt;code&gt;Reopen in Container&lt;/code&gt; button. You can also open the command palette (&lt;code&gt;Ctrl+Shift+P&lt;/code&gt;) and type &lt;code&gt;reopen in container&lt;/code&gt; and pick &lt;code&gt;Remote-Containers: Reopen in container&lt;/code&gt;. Then, it'll show a notification that it's installing the Code server and after a few moments it should open the project, and there will be an indication at the status bar that you are inside the container, like this:&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%2Fd8a9mwayw50e7f8leqo1.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%2Fd8a9mwayw50e7f8leqo1.png" title="Project opened"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then you will have all editor goodies, like this:&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%2Frnma2gluz7tsmkg6a6p9.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%2Frnma2gluz7tsmkg6a6p9.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And to debug, you just click the gutter to add a breakpoint, or press &lt;code&gt;F9&lt;/code&gt; at the desired line, like this:&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%2F6iy71kxn27d8838r74h2.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%2F6iy71kxn27d8838r74h2.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then go to the &lt;code&gt;Run&lt;/code&gt; view clicking at the side, and click the little green "play" button. The status bar should turn to orange to indicate the debugger is connected. If you have a breakpoint, like I did here, and navigate to the new API in the browser, you will be greeted with glorious debugging goodness, like this:&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%2Ffppt8xkui449cavm18xx.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%2Ffppt8xkui449cavm18xx.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each time you change some file the app will also reload, like this:&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%2F66kmx40xrhsnkhiyyx18.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%2F66kmx40xrhsnkhiyyx18.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if you need to run any Django administration commands, you can do so easily with the integrated terminal. Just open the menu &lt;code&gt;Terminal &amp;gt; New Terminal&lt;/code&gt; (&lt;code&gt;Ctrl+Shift+&amp;lt;BACKTICK&amp;gt;&lt;/code&gt;), like this:&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%2Fapy8i292493id3w7x254.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%2Fapy8i292493id3w7x254.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you find that the terminal did not have the venv activated, just click the &lt;code&gt;trash&lt;/code&gt; icon to kill it and open it up again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring for the frontend &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The configuration here is similar to the above. First create the directory &lt;code&gt;code/vscode/frontend&lt;/code&gt; and add the file &lt;code&gt;.devcontainer.json&lt;/code&gt;. I'm not configuring a project file here because there's no other folder besides the frontend project and to also use the settings files Quasar already creates when initializing the project. You could very well move everything to a project file just like we did with the backend.&lt;/p&gt;

&lt;p&gt;Here're the results:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/commit/644b7547322d903f7e4b33454b787e5d1d4c6f49" rel="noopener noreferrer"&gt;https://github.com/davidrios/example-docker-project/commit/644b7547322d903f7e4b33454b787e5d1d4c6f49&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You don't even need to rerun docker-compose, just open a new Code window, open the directory &lt;code&gt;code/vscode/frontend&lt;/code&gt; and click the &lt;code&gt;Reload in Container&lt;/code&gt; button. After opening the project, click the &lt;code&gt;ESLint&lt;/code&gt; button at the status bar and click to &lt;code&gt;allow&lt;/code&gt; when the dialog appears. No you will have a fully configured editor, like so:&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%2Fhu4cin7flkrlbmaimg74.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%2Fhu4cin7flkrlbmaimg74.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You get even autocomplete for things like Quasar components!&lt;/p&gt;

&lt;p&gt;If you wand to use the quasar-cli to do things like create a component, you just open the inbuilt terminal clicking on menu &lt;code&gt;Terminal &amp;gt; New Terminal&lt;/code&gt; (&lt;code&gt;Ctrl+Shift+&amp;lt;BACKTICK&amp;gt;&lt;/code&gt;), like this:&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%2F1r3aj6tl1zm31y8bddd7.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%2F1r3aj6tl1zm31y8bddd7.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now its time to debug!&lt;/p&gt;

&lt;p&gt;To do that, we create a &lt;code&gt;launch.json&lt;/code&gt; with debug tasks for VS Code. This is not so trivial for some reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VS Code has two Chrome debuggers, one newer that ships with it and the old one you can install from the marketplace. The problem is the newer is better, but for some reason, and I suspect it has to do with the remote container, it can't launch Chrome instances. The old one can launch instances, but is not so good. So to solve our problems we can use both!&lt;/li&gt;
&lt;li&gt;Since we have a custom port defined in our &lt;code&gt;.env&lt;/code&gt; file, we need some way to pass that to the debug task. Unfortunately the debug extensions runs at the local editor, not the remote one, so they don't have access to env vars. To solve that I added a little prompt when running the task that asks which port the app is running at, with the default from the env template for convenience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The added tasks file:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/blob/6e5195fd13d2aea79b1a31c265ddf60808d9a77e/code/frontend/.vscode/launch.json" rel="noopener noreferrer"&gt;https://github.com/davidrios/example-docker-project/blob/6e5195fd13d2aea79b1a31c265ddf60808d9a77e/code/frontend/.vscode/launch.json&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have three tasks which you will use depending on your browser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Attach to Chrome&lt;/code&gt;: This task uses the newer debugger to attach to a running Chrome instance with remote debugging enabled, and you don't need to install anything else. &lt;a href="https://blog.chromium.org/2011/05/remote-debugging-with-chrome-developer.html" rel="noopener noreferrer"&gt;Check here how to run Chrome with remote debugging enabled&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Launch Chrome&lt;/code&gt;: This one launches an instance of Chrome with remote debugging already enabled and using a new profile, so it doesn't affect your personal one. To use this task you need to have the &lt;a href="https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome" rel="noopener noreferrer"&gt;Debugger for Chrome extension installed&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Launch Firefox&lt;/code&gt;: And this launches an instance (or attaches to one if running) of Firefox with remote debugging enabled and also using a new profile. To use this task you need to have the &lt;a href="https://marketplace.visualstudio.com/items?itemName=firefox-devtools.vscode-firefox-debug" rel="noopener noreferrer"&gt;Debugger for Firefox extension installed&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's one more thing we can do to make our lives better while debugging. The default webpack config doesn't generate detailed sourcemaps, which makes debugging a pain and pretty much impossible to set breakpoints in transpiled files. We fix that by adding &lt;code&gt;devtool: 'eval-source-map'&lt;/code&gt; to its settings.&lt;/p&gt;

&lt;p&gt;Even then, for transpiled files like &lt;code&gt;.vue&lt;/code&gt; files, it generates a lot of variants of the same file with different hash names for each of the intermediate results, which is kind of annoying, so we fix that too by customizing the &lt;code&gt;output.devtoolModuleFilenameTemplate&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;Since this project is in Quasar, we do that the way its supposed to, by editing &lt;code&gt;quasar.conf.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It ends up looking like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/davidrios/example-docker-project/commit/1208e30bd280179df0780add22430af3a54c6c30" rel="noopener noreferrer"&gt;https://github.com/davidrios/example-docker-project/commit/1208e30bd280179df0780add22430af3a54c6c30&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can run the debugger by going to the &lt;code&gt;Run&lt;/code&gt; view, selecting a task and clicking "play":&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%2F3n67820af4t7c7npmz0x.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%2F3n67820af4t7c7npmz0x.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try adding some &lt;code&gt;console.log&lt;/code&gt;s and/or setting a breakpoint, running debug mode and using the app, then you should see something like this:&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%2Fsfbquglbieoj4m8bmdt3.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%2Fsfbquglbieoj4m8bmdt3.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Addendum &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I'm not a huge fan of doing too much inside the code editor, especially things which are much more flexible to do in the terminal, that's why I don't use the Code Docker extension to manage/run docker-compose for example. I also think it works better that way when you have a project with several containers with different apps, like in this guide.&lt;/p&gt;

&lt;p&gt;Since we are managing the compose flow manually, sometimes VS Code can show you this notification:&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%2Fl5wl8yn6nafoon3m7vo6.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%2Fl5wl8yn6nafoon3m7vo6.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I recommend you to just click &lt;code&gt;Ignore&lt;/code&gt;. If you need to rebuild things, just stop the compose running at the terminal and execute &lt;code&gt;docker-compose up --build&lt;/code&gt; again.&lt;/p&gt;

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

&lt;p&gt;There are a lot of things to improve and different choices you could make for the various pieces, these are the ones I made and that worked the best for me.&lt;/p&gt;

&lt;p&gt;Do note that this whole configuration is better suited to individuals and small teams, there are other, often mutually-exclusive, requirements for larger teams.&lt;/p&gt;

&lt;p&gt;This is all very development focused, so the container images are not real ones that you can use to deploy, but that is doable with a little more work.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>docker</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
