<?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: Fábio Ribeiro</title>
    <description>The latest articles on DEV Community by Fábio Ribeiro (@faabiosr).</description>
    <link>https://dev.to/faabiosr</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%2F168043%2F65977eb9-7341-477a-95fc-fa5ab5d99a8b.jpeg</url>
      <title>DEV Community: Fábio Ribeiro</title>
      <link>https://dev.to/faabiosr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/faabiosr"/>
    <language>en</language>
    <item>
      <title>Não crie logs de erros, trate-os</title>
      <dc:creator>Fábio Ribeiro</dc:creator>
      <pubDate>Mon, 26 May 2025 18:49:00 +0000</pubDate>
      <link>https://dev.to/faabiosr/nao-crie-logs-de-erros-trate-os-2n60</link>
      <guid>https://dev.to/faabiosr/nao-crie-logs-de-erros-trate-os-2n60</guid>
      <description>&lt;p&gt;Organização de código é um dos pontos principais para a manutenção, adição de novas regras de negócio e também de aprendizado. Um projeto, que ao longo do seu ciclo de desenvolvimento sofreu com mudanças drásticas, tende a ter um código menos organizado. E poder organizá-lo durante o desenvolvimento traz diversos benefícios.&lt;/p&gt;

&lt;p&gt;Pensando em como organizar um projeto ou código, noto que muitas vezes nós programadores deixamos passar pequenos detalhes, que podem até parecer supérfluos, e muitas vezes simples, mas que podem trazer um ganho real de como estruturamos o nosso projeto.&lt;/p&gt;

&lt;p&gt;Dessa vez, eu venho apresentar uma maneira simples de lidar com erros e logs em um projeto em Golang, o que vai ser demonstrado aqui também poderá ser aplicado em qualquer outra linguagem, framework ou projeto. Não é algo exclusivo em Golang, mas é um padrão que vejo em diversos projetos que trabalhei, em PHP, Python, Javascript e outros.&lt;/p&gt;

&lt;h2&gt;
  
  
  O projeto
&lt;/h2&gt;

&lt;p&gt;O projeto ou a aplicação em questão estava fazendo o tratamento de um erro retornado pelo banco de dados, o erro é retornado, mas também cria um log desse mesmo erro.&lt;/p&gt;

&lt;p&gt;Vamos ao exemplo (&lt;em&gt;de momento não se preocupe, algumas partes foram omitidas, mas tudo estará em um repositório no final do artigo&lt;/em&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;movies&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"encoding/json"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/labstack/echo/v4"&lt;/span&gt;
    &lt;span class="n"&gt;bolt&lt;/span&gt; &lt;span class="s"&gt;"go.etcd.io/bbolt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Datasource&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;     &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;bolt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;bucketName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"movies"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewDatasource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;bolt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Datasource&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Datasource&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ds&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Datasource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;Movie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Movie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;bolt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateBucketIfNotExists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Marshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Put&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como foi comentado, independente do erro retornado, um log será criado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Olhando rapidamente parece que não é nada fora do comum, mas isso é algo recorrente em diversos projetos que trabalhei.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Mas isso é um problema?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Mesmo parecendo inofensivo, isso nos faz pensar em alguns pontos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;É necessário criar um log uma vez que o erro é retornado?&lt;/li&gt;
&lt;li&gt;Erros como esse de banco de dados devem ser apresentados diretamente ao cliente (api client)?&lt;/li&gt;
&lt;li&gt;Como a aplicação lida com os erros retornados?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Existem pelo menos três pontos onde podem ser melhorados, vamos a cada um deles em passos separados, fazendo melhorias pontuais.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Removendo a criação do log
&lt;/h2&gt;

&lt;p&gt;Sendo honesto, essa etapa é a mais simples de todas, apenas vamos remover a criação de log e toda a dependência em torno dele.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/internal/movies/datasource.go b/internal/movies/datasource.go
&lt;/span&gt;&lt;span class="gd"&gt;--- a/internal/movies/datasource.go
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/internal/movies/datasource.go
&lt;/span&gt;&lt;span class="p"&gt;@@ -3,19 +3,17 @@&lt;/span&gt; package movies
 import (
        "encoding/json"
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-       "github.com/labstack/echo/v4"
&lt;/span&gt;        bolt "go.etcd.io/bbolt"
 )
&lt;span class="err"&gt;
&lt;/span&gt; type Datasource struct {
&lt;span class="gd"&gt;-       db     *bolt.DB
-       logger echo.Logger
&lt;/span&gt;&lt;span class="gi"&gt;+       db *bolt.DB
&lt;/span&gt; }
&lt;span class="err"&gt;
&lt;/span&gt; var bucketName = []byte("movies")
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-func NewDatasource(db *bolt.DB, logger echo.Logger) *Datasource {
-       return &amp;amp;Datasource{db: db, logger: logger}
&lt;/span&gt;&lt;span class="gi"&gt;+func NewDatasource(db *bolt.DB) *Datasource {
+       return &amp;amp;Datasource{db: db}
&lt;/span&gt; }
&lt;span class="err"&gt;
&lt;/span&gt; func (ds *Datasource) Store(m Movie) (Movie, error) {
&lt;span class="p"&gt;@@ -33,7 +31,6 @@&lt;/span&gt; func (ds *Datasource) Store(m Movie) (Movie, error) {
                return bucket.Put([]byte(m.ID), entry)
        })
        if err != nil {
&lt;span class="gd"&gt;-               ds.logger.Error(err)
&lt;/span&gt;                return m, err
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mas fica o questionamento, &lt;em&gt;não vamos criar logs em caso de erros?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sim, o log de erro será criado, vamos delegar essa funcionalidade para a camada de tratamento de erros (error handling), que é umas das partes cruciais de toda a aplicação. Por agora não vamos nos aprofundar nisso, baby steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Tratando e apresentando erros para os clientes (api clients)
&lt;/h2&gt;

&lt;p&gt;Agora começamos uma parte bem interessante. Para esse trecho deveríamos identificar o tipo de erro retornado e apresentar de uma maneira mais simples para o cliente, omitindo o erro original do banco de dados.&lt;/p&gt;

&lt;p&gt;Ao invés de responder para o cliente com a mensagem de erro &lt;code&gt;database is in read-only mode&lt;/code&gt;, podemos retornar &lt;code&gt;unable to store the movie&lt;/code&gt;, omitindo o erro original.&lt;/p&gt;

&lt;p&gt;No Golang é possível encapsular um erro para que contenha o erro a ser apresentado e erro original, a biblioteca padrão da linguagem já nos dá essa funcionalidade, mas de forma mais simplista, é o caso do &lt;code&gt;fmt.Errorf&lt;/code&gt;, mas para o nosso caso vamos criar o nosso próprio "wrap" de erro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/internal/errors/public.go b/internal/errors/public.go
&lt;/span&gt;&lt;span class="gd"&gt;--- /dev/null
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/internal/errors/public.go
&lt;/span&gt;&lt;span class="p"&gt;@@ -0,0 +1,29 @@&lt;/span&gt;
&lt;span class="gi"&gt;+package errors
+
+type publicErr struct {
+       err error
+       msg string
+}
+
+func (e *publicErr) Error() string {
+       return e.err.Error()
+}
+
+func (e *publicErr) Public() string {
+       return e.msg
+}
+
+func (e *publicErr) Unwrap() error {
+       return e.err
+}
+
+func Public(err error, msg string) error {
+       if err == nil {
+               return nil
+       }
+
+       return &amp;amp;publicErr{
+               err: err,
+               msg: msg,
+       }
+}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse novo wrap irá encapsular o erro original do banco de dados, e aceitará uma mensagem de error mais agradável que será exibida para o cliente.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/internal/movies/datasource.go b/internal/movies/datasource.go
&lt;/span&gt;&lt;span class="gd"&gt;--- a/internal/movies/datasource.go
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/internal/movies/datasource.go
&lt;/span&gt;&lt;span class="p"&gt;@@ -4,6 +4,8 @@&lt;/span&gt; import (
        "encoding/json"
&lt;span class="err"&gt;
&lt;/span&gt;        bolt "go.etcd.io/bbolt"
&lt;span class="gi"&gt;+
+       ierr "github.com/faabiosr/go-movies-demo/internal/errors"
&lt;/span&gt; )
&lt;span class="err"&gt;
&lt;/span&gt; type Datasource struct {
&lt;span class="p"&gt;@@ -31,7 +33,7 @@&lt;/span&gt; func (ds *Datasource) Store(m Movie) (Movie, error) {
                return bucket.Put([]byte(m.ID), entry)
        })
        if err != nil {
&lt;span class="gd"&gt;-               return m, err
&lt;/span&gt;&lt;span class="gi"&gt;+               return m, ierr.Public(err, "unable to save the movie")
&lt;/span&gt;        }
&lt;span class="err"&gt;
&lt;/span&gt;        return m, nil
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como ainda não estamos lidando com esse novo tipo de erro, o erro original ainda será exibido pois a função &lt;code&gt;Error&lt;/code&gt; do nosso wrap retorna o erro original. Para isso necessitamos criar a camada que lida com os erros de fato, ela sim, irá identificar o &lt;code&gt;publicErr&lt;/code&gt; wrap, criar o log do erro original e também apresentar a mensagem pública do erro.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Error handling
&lt;/h2&gt;

&lt;p&gt;Agora já não temos nenhum log na camada de negócio e também já preparamos o erro com o &lt;code&gt;publicErr&lt;/code&gt; wrap, só faltará identificar o erro e apresentar.&lt;/p&gt;

&lt;p&gt;Para esse exemplo, estamos usando o framework &lt;a href="https://echo.labstack.com" rel="noopener noreferrer"&gt;Echo&lt;/a&gt;, ele internamente tem a sua própria camada que lida com os erros, mas vamos criar uma nova.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/internal/errors/handler.go b/internal/errors/handler.go
&lt;/span&gt;&lt;span class="p"&gt;new file mode 100644
&lt;/span&gt;&lt;span class="gh"&gt;index 0000000..e2ad2d1
&lt;/span&gt;&lt;span class="gd"&gt;--- /dev/null
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/internal/errors/handler.go
&lt;/span&gt;&lt;span class="p"&gt;@@ -0,0 +1,42 @@&lt;/span&gt;
&lt;span class="gi"&gt;+package errors
+
+import (
+       "errors"
+       "net/http"
+
+       "github.com/labstack/echo/v4"
+)
+
+func ErrorHandler(logger echo.Logger) echo.HTTPErrorHandler {
+       return func(err error, ec echo.Context) {
+               var status int
+               var msg string
+
+               if ee := new(echo.HTTPError); errors.As(err, &amp;amp;ee) {
+                       status = ee.Code
+                       msg = ee.Error()
+
+                       var pe interface {
+                               Public() string
+                       }
+
+                       if errors.As(ee.Message.(error), &amp;amp;pe) {
+                               msg = pe.Public()
+                       }
+               }
+
+               if ec.Response().Committed {
+                       return
+               }
+
+               if ec.Request().Method == http.MethodHead {
+                       err = ec.NoContent(status)
+               } else {
+                       err = ec.JSON(status, echo.Map{"message": msg})
+               }
+
+               if err != nil {
+                       logger.Error(err)
+               }
+       }
+}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O &lt;code&gt;ErrorHandler&lt;/code&gt; irá verificar se o tipo de erro retornado é o padrão do &lt;code&gt;Echo&lt;/code&gt;, e depois se o erro definido segue a interface do &lt;code&gt;Public&lt;/code&gt;, com isso a nova mensagem será definida.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Usando o novo Error Handling
&lt;/h2&gt;

&lt;p&gt;Tudo pronto, o último passo é usar o novo pacote e ver o resultado, no exemplo abaixo, temos um único endpoint que retornará erro, em caso de o banco de dados estiver no modo leitura.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/labstack/echo/v4"&lt;/span&gt;
    &lt;span class="n"&gt;mw&lt;/span&gt; &lt;span class="s"&gt;"github.com/labstack/echo/v4/middleware"&lt;/span&gt;
    &lt;span class="n"&gt;glog&lt;/span&gt; &lt;span class="s"&gt;"github.com/labstack/gommon/log"&lt;/span&gt;
    &lt;span class="n"&gt;bolt&lt;/span&gt; &lt;span class="s"&gt;"go.etcd.io/bbolt"&lt;/span&gt;

    &lt;span class="n"&gt;ierr&lt;/span&gt; &lt;span class="s"&gt;"github.com/faabiosr/go-movies-demo/internal/errors"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/faabiosr/go-movies-demo/internal/movies"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPErrorHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ierr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;glog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bolt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"catalog.db"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="n"&gt;o400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;bolt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ReadOnly&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;ds&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;movies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDatasource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/movies"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ec&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;movies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Movie&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewHTTPError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusInternalServerError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCreated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8000"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ao executar o app, e fazer uma chamada, esse será o resultado final:&lt;/p&gt;

&lt;p&gt;Log de erros interno do servidor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2025-05-26T20:28:58.679194618+02:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"remote_ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"localhost:8000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"uri"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"/movies"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"user_agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"HTTPie/3.2.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"code=500, message=database is in read-only mode"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"latency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;61539&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"latency_human"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"61.539µs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"bytes_in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"bytes_out"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resposta dada ao cliente http:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt; &lt;span class="ne"&gt;Internal Server Error&lt;/span&gt;
&lt;span class="na"&gt;Content-Length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;39&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;
&lt;span class="na"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Mon, 26 May 2025 18:28:58 GMT&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"unable to save the movie"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Bem, é isso, tentei ao máximo resumir o conteúdo, e dar exemplos práticos ao longo do caminho. O isolamento de pequenas partes do nosso código facilitarão ao desenvolver uma nova funcionalidade, e claro, a nossa vida como dev.&lt;/p&gt;

&lt;p&gt;A aplicação completa usada no artigo está em &lt;a href="https://github.com/faabiosr/go-movies-demo" rel="noopener noreferrer"&gt;https://github.com/faabiosr/go-movies-demo&lt;/a&gt;, você encontrará tudo lá.&lt;/p&gt;

&lt;p&gt;Se você está afim de ter o seu próprio servidor, aqui vai um link de créditos para brincar na &lt;a href="https://m.do.co/c/67aea006f19f" rel="noopener noreferrer"&gt;Digital Ocean&lt;/a&gt; e criar os seus droplets.&lt;/p&gt;

&lt;p&gt;Recomendo a leitura das referências abaixo para entender um pouco mais sobre os erros no Golang:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://go.dev/blog/error-handling-and-go" rel="noopener noreferrer"&gt;Error handling and Go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pkg.go.dev/errors" rel="noopener noreferrer"&gt;Error Package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>logs</category>
      <category>errors</category>
      <category>programming</category>
    </item>
    <item>
      <title>Distribuindo uma aplicação Go sem o Docker</title>
      <dc:creator>Fábio Ribeiro</dc:creator>
      <pubDate>Fri, 27 Oct 2023 17:55:47 +0000</pubDate>
      <link>https://dev.to/faabiosr/distribuindo-uma-aplicacao-go-sem-o-docker-p73</link>
      <guid>https://dev.to/faabiosr/distribuindo-uma-aplicacao-go-sem-o-docker-p73</guid>
      <description>&lt;p&gt;Quando pensamos em escalabilidade de software, invariavelmente, em alguma eventualidade nos deparamos com a necessidade de distribuir nosso software.&lt;/p&gt;

&lt;p&gt;A grande maioria dos artigos e tutoriais que encontramos e que falam sobre distribuição de software, acabam descrevendo sobre como distribuir usando Kubernetes, Docker Swarm e às vezes preparando o bom e velho &lt;code&gt;docker-compose.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Dependendo do tamanho da sua aplicação, às vezes não é necessário ter um cluster de Kubernetes, também em alguns casos queremos obter todo o potencial do servidor, como memória, disco e rede, evitando ter uma camada extra entre a aplicação e a máquina.&lt;/p&gt;

&lt;p&gt;Nesse artigo, apresento um compilado de como distribuir a sua aplicação para ser executada diretamente no servidor, por exemplo na sua instância AWS EC2, Google Compute Engine, e quem sabe talvez no seu droplet na Digital Ocean (no final do artigo compartilho um cupom de crédito para você brincar).&lt;/p&gt;

&lt;p&gt;Tudo será apresentado em passos que serão incrementais, o empacotamento de uma aplicação em Golang, a preparação das dependências requeridas, a execução, e finalizamos com a atualização e tempo de inatividade.&lt;/p&gt;

&lt;p&gt;De forma simplificada, uma aplicação será desenvolvida, e para cada mudança, novas &lt;code&gt;tags&lt;/code&gt; serão criadas, e iremos focar o empacotamento e distribuição usando Ubuntu Linux (server). Ao final deixo o link onde você encontrará o projeto completo.&lt;/p&gt;

&lt;h2&gt;
  
  
  A aplicação
&lt;/h2&gt;

&lt;p&gt;Antes de tudo, precisaremos de uma aplicação,e ela guardará os nomes dos filmes e seu dia de lançamento. Na parte que cabe ao banco de dados, eles serão armazenados em um banco de dados em arquivo, e é requerido que seja definida uma variável de ambiente chamada &lt;code&gt;MOVIES_DB_PATH&lt;/code&gt;, onde contém a localização deste arquivo.&lt;/p&gt;

&lt;p&gt;Vamos para a aplicação em si (&lt;em&gt;de momento não se preocupe com as libs usadas, tudo estará no repositório no final do artigo&lt;/em&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
    &lt;span class="s"&gt;"os/signal"&lt;/span&gt;
    &lt;span class="s"&gt;"path/filepath"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/labstack/echo/v4"&lt;/span&gt;
    &lt;span class="n"&gt;bolt&lt;/span&gt; &lt;span class="s"&gt;"go.etcd.io/bbolt"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/faabiosr/go-movies-demo/internal/movies"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;appAddr&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.0.0.0:8000"&lt;/span&gt;
    &lt;span class="n"&gt;appName&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"moviez"&lt;/span&gt;
    &lt;span class="n"&gt;dbName&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"catalog.db"&lt;/span&gt;
    &lt;span class="n"&gt;dbPathEnv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"MOVIES_DB_PATH"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dbPathEnv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"env '%s' was not defined"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dbPathEnv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;dbPath&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dbPathEnv&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;dbName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Database connect&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bolt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dbPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="n"&gt;o600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;ds&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;movies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDatasource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// API Resources&lt;/span&gt;
    &lt;span class="n"&gt;movies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Routes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/movies"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Start server&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Infof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;appName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appAddr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"shutting down the service"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;

    &lt;span class="c"&gt;// Graceful shutdown&lt;/span&gt;
    &lt;span class="n"&gt;quit&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Interrupt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TODO&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Shutdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note o ponto que informamos anteriormente, a aplicação necessita da variável de ambiente &lt;code&gt;MOVIES_DB_PATH&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;dbName&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"catalog.db"&lt;/span&gt;
    &lt;span class="n"&gt;dbPathEnv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"MOVIES_DB_PATH"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;dbPath&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dbPathEnv&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;dbName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Database connect&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bolt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dbPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="n"&gt;o600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora que temos a aplicação de exemplo, é hora de compilar, e é bem simples, o resultado do comando abaixo será um binário com o nome de &lt;code&gt;movies&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go build &lt;span class="nt"&gt;-o&lt;/span&gt; movies ./cmd/movies
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com o binário em mãos, já podemos copiar e executar no servidor.&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;MOVIES_DB_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp ./movies
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pronto é isso! Guia finalizado!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;WTF?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;OK, calma, calma!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Mesmo com o binário criado, a distribuição não é fácil, copiar para o servidor todas as vezes que houver uma nova atualização e executar manualmente pode tornar-se complexo e chato.&lt;/p&gt;

&lt;p&gt;Vale ressaltar que não há uma forma consistente para a instalação, e não há como evitar que seja usada outra pasta no servidor, tornando o processo difícil de controlar e passível a erros.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Empacotando o binário
&lt;/h2&gt;

&lt;p&gt;Sabendo que a aplicação será executada em um Ubuntu Linux, temos a possibilidade de distribuir como um pacote &lt;code&gt;Debian (deb)&lt;/code&gt;, ou até mesmo como &lt;code&gt;snap&lt;/code&gt;, mas neste exemplo iremos focar no Debian.&lt;/p&gt;

&lt;p&gt;Empacotar como &lt;code&gt;.deb&lt;/code&gt; nos dá algumas vantagens, como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Poder controlar a versão do pacote.&lt;/li&gt;
&lt;li&gt;Executar scripts antes e depois da instalação/remoção do pacote.&lt;/li&gt;
&lt;li&gt;Adicionar arquivos extras.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Preparar um pacote Debian "na mão", não é uma tarefa muito simples, e para facilitar a nossa vida vamos usar o &lt;a href="https://github.com/goreleaser/goreleaser"&gt;GoReleaser&lt;/a&gt;, essa ferramenta maravilhosa, que internamente faz uso da é &lt;a href="https://github.com/goreleaser/nfpm"&gt;nFPM&lt;/a&gt;, responsável por criar pacotes Linux. Também é importante dizer que o GoReleaser não só nos ajuda a criar os pacotes Debian, mas também pacotes para Windows, MacOS, RPM, APK e muito mais.&lt;/p&gt;

&lt;p&gt;Em nosso projeto, vamos definir o arquivo &lt;a href="https://github.com/faabiosr/go-movies-demo/blob/v0.0.2/.goreleaser.yaml"&gt;.goreleaser.yaml&lt;/a&gt;, que contém as informações do pacote a ser gerado, atente-se para seção compartilhada abaixo e note que em &lt;code&gt;formats&lt;/code&gt; foi definido o &lt;code&gt;.deb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;nfpms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;movies&lt;/span&gt;
    &lt;span class="na"&gt;package_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;movies&lt;/span&gt;
    &lt;span class="na"&gt;file_name_template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;.ConventionalFileName&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Manages movie collection through API&lt;/span&gt;
    &lt;span class="na"&gt;license&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MIT&lt;/span&gt;
    &lt;span class="na"&gt;formats&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;deb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adicionalmente, foi configurado o &lt;a href="https://github.com/faabiosr/go-movies-demo/blob/main/.github/workflows/release.yml"&gt;Github Workflows&lt;/a&gt; onde contém os passos para compilar e distribuir em &lt;code&gt;.deb&lt;/code&gt;, e o resultado é esse:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6j94078vx93hkxjru026.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6j94078vx93hkxjru026.png" alt="contém um printscreen da lista de arquivos gerados pelo Goreleaser" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Todas as versões criadas estarão em &lt;a href="https://github.com/faabiosr/go-movies-demo/releases"&gt;releases&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Pronto! Agora sim temos um maior controle sobre o versionamento e distribuição da aplicação. De maneira simples e consistente podemos instalar em qualquer distro baseada em Debian.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Lidando com dependências (banco de dados em arquivo)
&lt;/h2&gt;

&lt;p&gt;Anteriormente foi mencionado que a aplicação necessita de uma pasta onde o banco de dados será criado, a pasta elegida será em &lt;code&gt;/var/lib/movies-demo&lt;/code&gt;, e para a criá-la vamos usar alguns dos hooks que pacote Debian nos fornece:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;postinst (post-install.sh)&lt;/code&gt;: é executado após instalar ou atualizar um pacote, esse hook ficará responsável por criar a pasta definida acima e também o usuário/grupo dessa pasta (&lt;em&gt;é recomendável que sempre tenha um usuário/grupo, ficando isolado dos demais&lt;/em&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;#!/bin/sh&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nv"&gt;MOVIES_DB_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/movies-demo
&lt;span class="nv"&gt;MOVIES_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;movies-demo

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"configure"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# creating user and group&lt;/span&gt;
    adduser &lt;span class="nt"&gt;--quiet&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;--home&lt;/span&gt; /nonexistent &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;--no-create-home&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;--disabled-password&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;--group&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MOVIES_USER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="c"&gt;# creating database folder&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;$MOVIES_DB_PATH&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$MOVIES_DB_PATH&lt;/span&gt;
        &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nv"&gt;$MOVIES_USER&lt;/span&gt;:&lt;span class="nv"&gt;$MOVIES_USER&lt;/span&gt; &lt;span class="nv"&gt;$MOVIES_DB_PATH&lt;/span&gt;
    &lt;span class="k"&gt;fi

    &lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Da mesma maneira que temos o &lt;code&gt;postinst&lt;/code&gt;, também temos um hook para quando removemos o pacote.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;postrm (post-remove-sh)&lt;/code&gt;: é executado quando removemos um pacote, e removerá a pasta apenas quando o arquivo de &lt;code&gt;catalog.db&lt;/code&gt; não existir.&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;#!/bin/sh&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nv"&gt;MOVIES_DB_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/movies-demo

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"remove"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MOVIES_DB_PATH&lt;/span&gt;&lt;span class="s2"&gt;/catalog.db"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Database file found and won't be removed."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Removing database folder."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
        &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-fr&lt;/span&gt; &lt;span class="nv"&gt;$MOVIES_DB_PATH&lt;/span&gt;
    &lt;span class="k"&gt;fi

    &lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scripts prontos, agora é só fazer a inclusão deles no &lt;code&gt;.goreleaser.yaml&lt;/code&gt; e quando empacotar e instalar a aplicação novamente, a pasta será criada:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/.goreleaser.yaml b/.goreleaser.yaml
&lt;/span&gt;&lt;span class="gd"&gt;--- a/.goreleaser.yaml
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/.goreleaser.yaml
&lt;/span&gt;&lt;span class="p"&gt;@@ -46,6 +46,9 @@&lt;/span&gt; nfpms:
     license: MIT
     formats:
       - deb
&lt;span class="gi"&gt;+    scripts:
+      postinstall: "env/debian/post-install.sh"
+      postremove: "env/debian/post-remove.sh"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Rodando a aplicação em background como um serviço (systemd)
&lt;/h2&gt;

&lt;p&gt;A pasta já está pronta, mas a execução pela linha de comando continua sendo manual, e para resolver essa questão, vamos executar a aplicação em background como um serviço. Para isso tiraremos proveito do &lt;a href="https://systemd.io/"&gt;systemd&lt;/a&gt;, que já vem instalado no Ubuntu Linux.&lt;/p&gt;

&lt;p&gt;Em poucas palavras, o &lt;code&gt;systemd&lt;/code&gt; é um conjunto de blocos de construção para uma sistema Linux, ele fornece um gerenciador de sistema e serviço, e é justamente do segundo ponto que necessitamos.&lt;/p&gt;

&lt;p&gt;Para um serviço é requerido que seja criado um arquivo onde contém as referências para um recurso que o sistema saberá como operar e gerenciar, chamado de &lt;code&gt;unit&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;O unit que usaremos é o &lt;code&gt;service&lt;/code&gt;, que descreve como gerenciar um serviço ou aplicação no servidor:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;movies.service&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Manages movie collection through API&lt;/span&gt;
&lt;span class="py"&gt;Documentation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://github.com/faabiosr/go-movies-demo"&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;EnvironmentFile&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/etc/default/movies&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/movies&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;on-failure&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;movies-demo&lt;/span&gt;
&lt;span class="py"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;movies-demo&lt;/span&gt;
&lt;span class="py"&gt;KillSignal&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;SIGINT&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Na seção &lt;code&gt;Service&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EnvironmentFile: arquivo que contém as variáveis de ambiente.&lt;/li&gt;
&lt;li&gt;ExecStart: caminho do binário.&lt;/li&gt;
&lt;li&gt;User/Group: usaremos o mesmo criado anteriormente.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Precisaremos agora criar um arquivo que contém a variável de ambiente usada pela aplicação:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;movies.conf&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;MOVIES_DB_PATH="/var/lib/movies-demo"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uma vez finalizada a criação dos arquivos necessários para o systemd, é imprescindível atualizar os hooks do debian para que o mesmo seja ativado e incializado após a instalação:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;postint (post-install.sh)&lt;/code&gt;: ativa o serviço se não foi ativo, e inicia ou reinicia caso já esteja rodando.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/env/debian/post-install.sh b/env/debian/post-install.sh
&lt;/span&gt;&lt;span class="gd"&gt;--- a/env/debian/post-install.sh
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/env/debian/post-install.sh
&lt;/span&gt;&lt;span class="p"&gt;@@ -4,6 +4,7 @@&lt;/span&gt; set -e
&lt;span class="err"&gt;
&lt;/span&gt; MOVIES_DB_PATH=/var/lib/movies-demo
 MOVIES_USER=movies-demo
&lt;span class="gi"&gt;+MOVIES_SERVICE=movies.service
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt; if [ "$1" = "configure" ]; then
     # creating user and group
&lt;span class="p"&gt;@@ -20,5 +21,25 @@&lt;/span&gt; if [ "$1" = "configure" ]; then
         chown $MOVIES_USER:$MOVIES_USER $MOVIES_DB_PATH
     fi
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-    exit 0
&lt;/span&gt;&lt;span class="gi"&gt;+    # enable systemd service
+    deb-systemd-helper unmask $MOVIES_SERVICE &amp;gt;/dev/null || true
+
+    if deb-systemd-helper --quiet was-enabled $MOVIES_SERVICE; then
+        deb-systemd-helper enable $MOVIES_SERVICE &amp;gt;/dev/null || true
+    else
+        deb-systemd-helper update-state $MOVIES_SERVICE &amp;gt;/dev/null || true
+    fi
+
+    # starting service
+    if [ -d /run/systemd/system ]; then
+        systemctl --system daemon-reload &amp;gt;/dev/null || true
+
+        if [ -n "$2" ]; then
+            _dh_action=restart
+        else
+            _dh_action=start
+        fi
+
+        deb-systemd-invoke $_dh_action $MOVIES_SERVICE &amp;gt;/dev/null || true
+    fi
&lt;/span&gt; fi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;postrm (post-remove.sh)&lt;/code&gt;: a nova adição irá reiniciar o serviço do próprio systemd, e o serviço só será removido caso o usuário opte por uma remoção completa.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/env/debian/post-remove.sh b/env/debian/post-remove.sh
&lt;/span&gt;&lt;span class="gd"&gt;--- a/env/debian/post-remove.sh
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/env/debian/post-remove.sh
&lt;/span&gt;&lt;span class="p"&gt;@@ -3,6 +3,7 @@&lt;/span&gt;
 set -e
&lt;span class="err"&gt;
&lt;/span&gt; MOVIES_DB_PATH=/var/lib/movies-demo
&lt;span class="gi"&gt;+MOVIES_SERVICE=movies.service
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt; if [ "$1" = "remove" ]; then
     if [ -f "$MOVIES_DB_PATH/catalog.db" ]; then
&lt;span class="p"&gt;@@ -12,5 +13,16 @@&lt;/span&gt; if [ "$1" = "remove" ]; then
         rm -fr $MOVIES_DB_PATH
     fi
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-    exit 0
&lt;/span&gt;&lt;span class="gi"&gt;+    # disabling service
+    if [ -d /run/systemd/system ]; then
+        systemctl --system daemon-reload &amp;gt;/dev/null || true
+    fi
+
+    deb-systemd-helper mask $MOVIES_SERVICE &amp;gt;/dev/null || true
+fi
+
+if [ "$1" = "purge" ]; then
+    # disabling service
+    deb-systemd-helper purge $MOVIES_SERVICE &amp;gt;/dev/null || true
+    deb-systemd-helper unmask $MOVIES_SERVICE &amp;gt;/dev/null || true
&lt;/span&gt; fi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adicionalmente criamos o &lt;code&gt;prerm&lt;/code&gt; hook, que é executado antes de remover o pacote, e será ele responsável por finalizar a execução da aplicação, assim removemos o pacote de forma segura:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;prerm (pre-remove.sh)&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nv"&gt;MOVIES_SERVICE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;movies.service

&lt;span class="c"&gt;# stopping service&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; /run/systemd/system &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;deb-systemd-invoke stop &lt;span class="nv"&gt;$MOVIES_SERVICE&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora é só incluir todos os arquivos no pacote Debian atualizado o &lt;code&gt;.goreleaser.yaml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/.goreleaser.yaml b/.goreleaser.yaml
&lt;/span&gt;&lt;span class="gd"&gt;--- a/.goreleaser.yaml
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/.goreleaser.yaml
&lt;/span&gt;&lt;span class="p"&gt;@@ -46,7 +46,14 @@&lt;/span&gt; nfpms:
     license: MIT
     formats:
       - deb
&lt;span class="gi"&gt;+    contents:
+      - src: "env/debian/movies.service"
+        dst: "/lib/systemd/system/movies.service"
+      - src: "env/debian/movies.conf"
+        dst: "/etc/default/movies"
+        type: config
&lt;/span&gt;     scripts:
&lt;span class="gi"&gt;+      preremove: "env/debian/pre-remove.sh"
&lt;/span&gt;       postinstall: "env/debian/post-install.sh"
       postremove: "env/debian/post-remove.sh"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quando o pacote for instalado no sistema operacional, automaticamente será copiado os arquivos da seção &lt;code&gt;contents&lt;/code&gt; para os respectivos destinos, fará o registro no systemd e iniciará automaticamente.&lt;/p&gt;

&lt;p&gt;Poderia dizer que o empacotamento e a distribuição do aplicativo está finalizado, mas ainda temos um último problema para ser resolvido, vejamos na última parte.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Atualização e tempo de inatividade
&lt;/h2&gt;

&lt;p&gt;Instalar ou atualizar ficou extremamente simples e com um controle mais rígido, todavia, a aplicação pode ficar inativa durante a atualização. Isso ocorre pelo simples fato de finalizar o serviço e instalar uma nova versão, e tendo uma degradação da disponibilidade.&lt;/p&gt;

&lt;p&gt;Felizmente, isso pode ser contornado ainda usando o systemd, através de um outro unit, o &lt;code&gt;socket&lt;/code&gt;. Esse arquivo de unit codifica a informação sobre um soquete de rede ou arquivo, controlado e supervisionado, para uma ativação baseada em sockets.&lt;/p&gt;

&lt;p&gt;Vale lembrar que os unit &lt;code&gt;sockets&lt;/code&gt; não iniciam os serviços por conta própria, em vez disso, eles apenas esperam e escutam um endereço &lt;code&gt;IP:PORT&lt;/code&gt;, ou um &lt;code&gt;Unix&lt;/code&gt; socket, e quando algo se conecta a ele, o serviço ao qual o socket se destina será iniciado e a conexão é entregue a ele. Já que nossa aplicação lida com requisições HTTP, podemos usá-lo.&lt;/p&gt;

&lt;p&gt;Alguns passos adicionais precisarão ser concluídos, como a criação e modificação dos units, alteração dos hooks, e uma mudança na aplicação, pois ela precisa suportar essa funcionalidade.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;movies.socket&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Manages movie collection through API&lt;/span&gt;
&lt;span class="py"&gt;Documentation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://github.com/faabiosr/go-movies-demo"&lt;/span&gt;

&lt;span class="nn"&gt;[Socket]&lt;/span&gt;
&lt;span class="py"&gt;ListenStream&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;8000&lt;/span&gt;
&lt;span class="py"&gt;SocketUser&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;movies-demo&lt;/span&gt;
&lt;span class="py"&gt;SocketGroup&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;movies-demo&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;sockets.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aliás, é necessário atualizar o &lt;code&gt;movies.service&lt;/code&gt; e informar que o unit socket é requerido:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/env/debian/movies.service b/env/debian/movies.service
&lt;/span&gt;&lt;span class="gd"&gt;--- a/env/debian/movies.service
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/env/debian/movies.service
&lt;/span&gt;&lt;span class="p"&gt;@@ -1,6 +1,8 @@&lt;/span&gt;
 [Unit]
 Description=Manages movie collection through API
 Documentation="https://github.com/faabiosr/go-movies-demo"
&lt;span class="gi"&gt;+After=network.target
+Requires=movies.socket
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt; [Service]
 EnvironmentFile=/etc/default/movies
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alteramos também os hooks do Debian:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;postinst (post-install.sh)&lt;/code&gt;: também fará o registro do unit socket e só reiniciará o serviço caso houver uma atualização no pacote.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/env/debian/post-install.sh b/env/debian/post-install.sh
&lt;/span&gt;&lt;span class="gd"&gt;--- a/env/debian/post-install.sh
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/env/debian/post-install.sh
&lt;/span&gt;&lt;span class="p"&gt;@@ -5,6 +5,7 @@&lt;/span&gt; set -e
 MOVIES_DB_PATH=/var/lib/movies-demo
 MOVIES_USER=movies-demo
 MOVIES_SERVICE=movies.service
&lt;span class="gi"&gt;+MOVIES_SOCKET=movies.socket
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt; if [ "$1" = "configure" ]; then
     # creating user and group
&lt;span class="p"&gt;@@ -30,16 +31,24 @@&lt;/span&gt; if [ "$1" = "configure" ]; then
         deb-systemd-helper update-state $MOVIES_SERVICE &amp;gt;/dev/null || true
     fi
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+    # enable systemd socket
+    deb-systemd-helper unmask $MOVIES_SOCKET &amp;gt;/dev/null || true
+
+    if deb-systemd-helper --quiet was-enabled $MOVIES_SOCKET; then
+        deb-systemd-helper enable $MOVIES_SOCKET &amp;gt;/dev/null || true
+    else
+        deb-systemd-helper update-state $MOVIES_SOCKET &amp;gt;/dev/null || true
+    fi
+
&lt;/span&gt;     # starting service
     if [ -d /run/systemd/system ]; then
         systemctl --system daemon-reload &amp;gt;/dev/null || true
&lt;span class="err"&gt;
&lt;/span&gt;         if [ -n "$2" ]; then
&lt;span class="gd"&gt;-            _dh_action=restart
&lt;/span&gt;&lt;span class="gi"&gt;+            deb-systemd-invoke restart $MOVIES_SERVICE &amp;gt;/dev/null || true
&lt;/span&gt;         else
&lt;span class="gd"&gt;-            _dh_action=start
&lt;/span&gt;&lt;span class="gi"&gt;+            deb-systemd-invoke start $MOVIES_SOCKET &amp;gt;/dev/null || true
&lt;/span&gt;         fi
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-        deb-systemd-invoke $_dh_action $MOVIES_SERVICE &amp;gt;/dev/null || true
&lt;/span&gt;     fi
 fi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;prerm (pre-remove.sh)&lt;/code&gt;: quando seja feita uma atualização, apenas o serviço será desligado, finalizará o socket, e o serviço apenas na remoção.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/env/debian/pre-remove.sh b/env/debian/pre-remove.sh
&lt;/span&gt;&lt;span class="gd"&gt;--- a/env/debian/pre-remove.sh
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/env/debian/pre-remove.sh
&lt;/span&gt;&lt;span class="p"&gt;@@ -3,8 +3,18 @@&lt;/span&gt;
 set -e
&lt;span class="err"&gt;
&lt;/span&gt; MOVIES_SERVICE=movies.service
&lt;span class="gi"&gt;+MOVIES_SOCKET=movies.socket
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-# stopping service
-if [ -d /run/systemd/system ]; then
-    deb-systemd-invoke stop $MOVIES_SERVICE &amp;gt;/dev/null || true
&lt;/span&gt;&lt;span class="gi"&gt;+if [ "$1" = "remove" ]; then
+    # stopping service and socket
+    if [ -d /run/systemd/system ]; then
+        deb-systemd-invoke stop $MOVIES_SERVICE $MOVIES_SOCKET &amp;gt;/dev/null || true
+    fi
+fi
+
+if [ "$1" = "upgrade" ]; then
+    # stopping service
+    if [ -d /run/systemd/system ]; then
+        deb-systemd-invoke stop $MOVIES_SERVICE &amp;gt;/dev/null || true
+    fi
&lt;/span&gt; fi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No arquivo &lt;code&gt;.goreleaser.yaml&lt;/code&gt;, foi incluído o arquivo &lt;code&gt;.socket&lt;/code&gt;, na seção contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/.goreleaser.yaml b/.goreleaser.yaml
&lt;/span&gt;&lt;span class="gd"&gt;--- a/.goreleaser.yaml
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/.goreleaser.yaml
&lt;/span&gt;&lt;span class="p"&gt;@@ -49,6 +49,8 @@&lt;/span&gt; nfpms:
     contents:
       - src: "env/debian/movies.service"
         dst: "/lib/systemd/system/movies.service"
&lt;span class="gi"&gt;+      - src: "env/debian/movies.socket"
+        dst: "/lib/systemd/system/movies.socket"
&lt;/span&gt;       - src: "env/debian/movies.conf"
         dst: "/etc/default/movies"
         type: config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Independente de configurar o systemd, a aplicação ainda não está preparada para fazer uso de sockets, para isso vamos adicionar o suporte de ativação de sockets.&lt;/p&gt;

&lt;p&gt;A equipe do &lt;a href="https://github.com/coreos"&gt;CoreOS&lt;/a&gt; desenvolveu o pacote &lt;a href="https://github.com/coreos/go-systemd"&gt;go-systemd&lt;/a&gt;, nele contém várias ferramentas para integrar com o systemd, entre eles o &lt;code&gt;activation&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;main.go&lt;/code&gt;: inclusão do &lt;code&gt;activation&lt;/code&gt; e integração com o servidor http.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/cmd/movies/main.go b/cmd/movies/main.go
&lt;/span&gt;&lt;span class="gd"&gt;--- a/cmd/movies/main.go
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/cmd/movies/main.go
&lt;/span&gt;&lt;span class="p"&gt;@@ -7,6 +7,7 @@&lt;/span&gt; import (
    "path/filepath"
    "time"
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+   "github.com/coreos/go-systemd/activation"
&lt;/span&gt;    "github.com/labstack/echo/v4"
    bolt "go.etcd.io/bbolt"
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;@@ -46,7 +47,7 @@&lt;/span&gt; func main() {
    e.Logger.Infof("%s service", appName)
&lt;span class="err"&gt;
&lt;/span&gt;    go func() {
&lt;span class="gd"&gt;-       if err := e.Start(appAddr); err != nil {
&lt;/span&gt;&lt;span class="gi"&gt;+       if err := start(e, appAddr); err != nil {
&lt;/span&gt;            e.Logger.Info("shutting down the service")
        }
    }()
&lt;span class="p"&gt;@@ -64,3 +65,16 @@&lt;/span&gt; func main() {
        e.Logger.Fatal(err)
    }
 }
&lt;span class="gi"&gt;+
+func start(e *echo.Echo, host string) error {
+   listeners, err := activation.Listeners()
+   if err != nil {
+       return nil
+   }
+
+   if len(listeners) &amp;gt; 0 {
+       e.Listener = listeners[0]
+   }
+
+   return e.Start(host)
+}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com essa parte final, a aplicação terá garantias de disponibilidade durante uma reinicialização ou atualização, e estará completamente funcional para distribuir.&lt;/p&gt;

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

&lt;p&gt;Agora nós sabemos como empacotar e distribuir o aplicativo, seguindo um modelo onde podemos versionar, preparar as dependências, e garantir disponibilidade.&lt;/p&gt;

&lt;p&gt;Acredito que os pontos compartilhados, não tem um curva de dificuldade alta, mas sim, pontos estratégicos para futura manutenção do aplicativo, tal qual, reduzir a complexidade na hora de distribuir a aplicação, e o ponto central é tirar proveito das ferramentas que estão disponíveis no sistema operacional que será rodado. &lt;/p&gt;

&lt;p&gt;A aplicação completa usada no artigo está em &lt;a href="https://github.com/faabiosr/go-movies-demo"&gt;https://github.com/faabiosr/go-movies-demo&lt;/a&gt;, você encontrará tudo lá.&lt;/p&gt;

&lt;p&gt;Como prometido, aqui vai um link de créditos para brincar na &lt;a href="https://m.do.co/c/67aea006f19f"&gt;Digital Ocean&lt;/a&gt; e criar os seus droplets.&lt;/p&gt;

&lt;p&gt;Recomendo a leitura das referências abaixo para entender um pouco mais sobre o systemd, e os hooks do Debian:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/understanding-systemd-units-and-unit-files"&gt;Understanding Systemd Units and Unit Files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.freedesktop.org/software/systemd/man/latest/systemd.html#"&gt;Systemd system and service manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://manpages.debian.org/bullseye/dpkg-dev/deb-prerm.5.en.html"&gt;deb-prerm&lt;/a&gt;, &lt;a href="https://manpages.debian.org/bullseye/dpkg-dev/deb-postinst.5.en.html"&gt;deb-postinst&lt;/a&gt;, &lt;a href="https://manpages.debian.org/bullseye/dpkg-dev/deb-postrm.5.en.html"&gt;deb-postrm&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>docker</category>
      <category>linux</category>
      <category>braziliandevs</category>
    </item>
  </channel>
</rss>
