<?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: Virtual360</title>
    <description>The latest articles on DEV Community by Virtual360 (@v360).</description>
    <link>https://dev.to/v360</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%2Forganization%2Fprofile_image%2F4565%2F329c24df-2f31-46bd-bdb7-b0616ff9cf87.png</url>
      <title>DEV Community: Virtual360</title>
      <link>https://dev.to/v360</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/v360"/>
    <language>en</language>
    <item>
      <title>Como aceleramos em 90% a execução das nossas migrações em Rails</title>
      <dc:creator>Augusto Pagnossim Frigo</dc:creator>
      <pubDate>Wed, 03 Sep 2025 21:04:09 +0000</pubDate>
      <link>https://dev.to/v360/como-aceleramos-em-90-a-execucao-das-nossas-migracoes-em-rails-1kdn</link>
      <guid>https://dev.to/v360/como-aceleramos-em-90-a-execucao-das-nossas-migracoes-em-rails-1kdn</guid>
      <description>&lt;p&gt;&lt;em&gt;Nota: Este post também está disponível &lt;a href="https://dev.to/v360/how-we-sped-up-our-rails-migration-setup-in-90-8be"&gt;em inglês&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ao usar Rails, todos precisamos criar, deletar ou reverter migrations, já que são um recurso necessário para estruturar o banco de dados ao construir sua aplicação. Migrations, no entanto, enfrentam um problema de escalabilidade: quanto mais migrations são adicionadas, mais tempo leva para executá-las para refletir as mudanças no schema do banco de dados. Além disso, se você troca de branch e executa alguma migration, seu schema.rb pode ficar com resquícios de outras branches, o que pode forçar você a recriar todo o banco de dados. Esse processo não é particularmente problemático em projetos pequenos, mas no nosso caso, com 6 anos de migrations, reconstruir o banco demorava muito. Além disso, como usamos muitos bancos de dados diferentes com o mesmo schema, criar novos bancos estava ficando lento, tornando a criação de novos ambientes mais trabalhosa do que deveria.&lt;/p&gt;

&lt;p&gt;Ao buscar soluções para esse problema, a primeira ideia pode ser tentar otimizar o código das migrations de alguma forma. Otimizar o código das migrations ajuda em parte, mas não traria uma melhoria significativa na velocidade.&lt;/p&gt;

&lt;p&gt;Primeiro, precisamos considerar que, por padrão, cada migration no Rails é executada em uma nova transaction, o que faz sentido. Você não quer lidar com uma migration pela metade em seu sistema de produção. No entanto, essa abordagem cria problemas de performance, já que executar migrations sequencialmente, cada uma em sua própria transaction, pode ficar muito lento conforme o número de migrations aumenta. No nosso caso, precisamos criar novos ambientes frequentemente, e esses novos ambientes precisam ser migrados e populados do zero. Além disso, muitas vezes os desenvolvedores podem precisar trocar de uma branch para outra e eles precisam reconstruir todo o banco de dados se não quiserem incluir resquícios indesejados no schema.rb.&lt;/p&gt;

&lt;p&gt;Além da questão da transaction, quando rodamos uma migration, o Rails adiciona seu timestamp na tabela schema_migrations. Para verificar se uma migration já foi executada, o Rails pega o timestamp da migration e tenta encontrar esse timestamp na tabela. Se encontrar, a migration é pulada; caso contrário, ela é executada. Note que o Rails verifica o timestamp da migration em relação ao conteúdo da tabela, mas o inverso não ocorre. Ou seja, não há problema em ter entradas na tabela que não existem na pasta migrate, e podemos adicionar um timestamp manualmente se não quisermos rodar uma migration em um ambiente específico.&lt;/p&gt;

&lt;p&gt;Percebemos que, embora nossas migrations fossem muito lentas, o schema:load era rápido, muito mais rápido que executar as migrations. Porém, a task schema:load não era adequada para ambientes de produção. O schema:load não é capaz de criar views nem executar código Ruby presente nas migrations. Por exemplo, se tivéssemos um código SQL criando uma view, ela não seria criada no schema:load. Além disso, algumas migrations executavam código Ruby para criar registros (&lt;code&gt;User.create!(name: 'John')&lt;/code&gt;, por exemplo), e seria arriscado ignorar essas criações sem garantir que estavam presentes no seeds.&lt;br&gt;
O desafio, então, era carregar o schema e ainda assim criar esses registros que só as migrations conseguiam. Trouxemos, assim, uma solução que engloba o melhor dos dois lados.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Atenção: considere que essa solução remove a vantagem de ser agnóstico em relação ao banco de dados que o schema.rb oferece. Ao gerar o arquivo .sql, você está criando um arquivo que funciona para o seu adaptador de banco atual, não necessariamente para outros. Além disso, essa solução pode não ser recomendada se você não tem um grande problema com migrations lentas, especialmente porque o Rails não possui suporte oficial a esse tipo de otimização, e manter o histórico e a reversibilidade das migrations pode ser melhor na maioria dos casos.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A primeira ideia foi criar um arquivo .sql que fizesse o mesmo que as migrations e então carregá-lo. Pensamos em usar o structure.sql como solução, já que o schema.rb não reflete a criação de views, mas o structure.sql sim. Também precisávamos limitar quais migrations seriam transformadas em SQL puro, pois queríamos poder reverter facilmente as mais recentes se necessário, e garantir que todos os ambientes já tivessem rodado as migrations que seriam convertidas. Como percebemos que todos os ambientes já tinham rodado todas as migrations de 2024, decidimos manter todas até 2024 como baseline. Todas as migrations de 2025 em diante não seriam convertidas para SQL puro e continuariam sendo executadas normalmente.&lt;/p&gt;

&lt;p&gt;No entanto, essa solução tinha um problema: o structure.sql não reflete registros criados ou atualizados durante as migrations. Embora seja esperado que os desenvolvedores sempre criem uma cópia dos updates/creates no seeds.rb quando necessário em novos ambientes, não havia garantia de que todo create/update que deveria ser portado estava refletido no seeds. Precisávamos então rastrear quais migrations tinham esse problema, mas tínhamos literalmente milhares de migrations e ler uma a uma não era viável nem seguro. Por isso, precisávamos rastrear automaticamente quais migrations faziam insert ou update, para pelo menos excluir da análise as que não criavam registros, que eram a maioria. Criamos então um initializer que só roda durante o processo de migration e loga quais migrations fazem esse tipo de operação. Note que esse initializer foi gerado por IA e não segue as melhores práticas, mas foi usado uma vez e removido do código, então tudo bem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config/initializers/migration_data_logger.rb

if defined?(ActiveRecord::Migration)
  module MigrationDataLogger
    class &amp;lt;&amp;lt; self
      attr_accessor :enabled

      def start_logging
        @enabled = true
        @log_file = File.open(Rails.root.join('log/migration_data_changes.log'), 'a')
        @current_migration = nil
        puts 'Migration logging started - writing to log/migration_data_changes.log'
      end

      def stop_logging
        @enabled = false
        @log_file&amp;amp;.close
        puts 'Migration logging stopped'
      end

      def log(message)
        return unless @enabled

        @log_file.puts "[#{Time.current}] #{@current_migration}: #{message}"
        @log_file.flush
      end

      def set_migration(migration)
        @current_migration = "#{migration.version} - #{migration.name}"
      end
    end
  end

  ActiveSupport::Notifications.subscribe('sql.active_record') do |_, started, finished, _, payload|
    MigrationDataLogger.log("#{payload[:sql]}") if MigrationDataLogger.enabled &amp;amp;&amp;amp; payload[:sql] =~ /^(INSERT|UPDATE|DELETE)/i
  end

  ActiveRecord::Migration.prepend(Module.new do
    def migrate(direction)
      MigrationDataLogger.start_logging
      MigrationDataLogger.set_migration(self)
      super
      MigrationDataLogger.stop_logging
    end
  end)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com os nomes dessas migrations em mãos, reduzimos de milhares para apenas 70 o número de migrations que precisaríamos analisar manualmente. Foi um número bom para análise manual, então não avançamos mais com o script, mas uma forma mais automatizada seria capturar todos os SQLs executados e descartar os que afetaram zero linhas. Depois disso, movemos todo o código que estava em migrations mas funcionava como seed para o db/seeds.rb, e finalmente pudemos nos livrar das migrations antigas sem nos preocupar com os registros criados.&lt;/p&gt;

&lt;p&gt;Após mapear o que deveria ser movido para o seeds, começamos o processo de obter o SQL pré-2025. O primeiro passo foi adicionar a seguinte linha ao application.rb:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config.active_record.schema_format = :sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depois, movemos todas as migrations pós-2024 para outra pasta (para não serem executadas), e reconstruímos o banco. Assim, obtivemos um arquivo SQL que refletia todas as migrations pré-2025 e podia ser facilmente carregado. Esse novo arquivo, chamado structure.sql, foi renomeado para structure_baseline.sql.&lt;br&gt;
Depois disso, deletamos todas as migrations pré-2025 e devolvemos as pós-2024 para a pasta migrate.&lt;br&gt;
Em seguida, geramos manualmente uma migration com timestamp do final de 2024, que seria executada após a última migration de 2024. Por exemplo, se a última migration tinha timestamp 20241230xxxxx, geraríamos uma com timestamp 20241231xxxx. Inserimos esse timestamp em todos os ambientes existentes editando diretamente a tabela schema_migrations. Assim, conseguimos ter uma migration que carrega todo o conteúdo do banco, mas nunca é executada em ambientes já existentes. Lembre-se: para funcionar, é preciso garantir que todos os ambientes já rodaram todas as migrations que estão sendo transformadas em SQL puro.&lt;br&gt;
Com esses preparativos, criamos a seguinte migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class LoadBaselineSchema &amp;lt; ActiveRecord::Migration[7.0]
  disable_ddl_transaction!

  def change
    ActiveRecord::Base.connection.execute(File.read(Rails.root.join('db/structure_baseline.sql')))
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Atenção: repare que o disable_ddl_transaction! faz a migration rodar sem transação no banco, o que pode ser um problema dependendo do seu contexto. Provavelmente você vai querer remover essa linha (ou pelo menos condicionar para rodar só em ambiente local). Optamos por usar porque acelera bastante o carregamento do .sql e só vai rodar em ambientes novos. Se algum problema acontecer durante o carregamento, basta recriar o banco, já que é um ambiente novo.&lt;br&gt;
Depois disso, removemos a linha adicionada no application.rb, já que a mudança definitiva do schema.rb para structure.sql seria feita em outro pull request para revisão adequada. Você pode explicitar no application.rb que quer usar ruby ao invés de sql, se preferir:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config.active_record.schema_format = :ruby
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Queríamos então rodar as migrations para gerar novamente o schema.rb. Porém, ao tentar carregar o structure_baseline.sql, o Rails tentava recriar as tabelas padrão — &lt;code&gt;ar_internal_metadata&lt;/code&gt; e &lt;code&gt;schema_migrations&lt;/code&gt;. Modificamos manualmente as linhas do structure.sql &lt;br&gt;
&lt;code&gt;CREATE TABLE public.ar_internal_metadata&lt;/code&gt; e &lt;code&gt;CREATE TABLE public.schema_migrations&lt;/code&gt; para incluir um &lt;code&gt;IF NOT EXISTS&lt;/code&gt;.&lt;br&gt;
Também tivemos problemas com os índices dessas tabelas, e adicionamos condições para só criar os índices se eles não existirem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ALTER TABLE ONLY public.ar_internal_metadata
    ADD CONSTRAINT ar_internal_metadata_pkey PRIMARY KEY (key);
ALTER TABLE ONLY public.schema_migrations
    ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;se tornou&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM pg_constraint
        WHERE conname = 'ar_internal_metadata_pkey'
    ) THEN
        ALTER TABLE ONLY public.ar_internal_metadata
            ADD CONSTRAINT ar_internal_metadata_pkey PRIMARY KEY (key);
    END IF;
END
$$;

DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM pg_constraint
        WHERE conname = 'schema_migrations_pkey'
    ) THEN
        ALTER TABLE ONLY public.schema_migrations
            ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version);
    END IF;
END
$$;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deletar essas criações de tabela e índices pode ser uma solução melhor, já que o Rails as cria automaticamente, mas não testamos essa abordagem. A partir daí, o db:migrate deve funcionar.&lt;br&gt;
Você também pode precisar deletar a linha &lt;code&gt;COMMENT ON EXTENSION pg_trgm IS 'text similarity measurement and index searching based on trigrams';&lt;/code&gt; também caso esteja rodando com um usuário sem permissão para comentar extensões.&lt;/p&gt;

&lt;p&gt;Com essas modificações, nosso comando db:migrate ficou muito mais rápido, mostrando resultados de cerca de 90% de otimização.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ruby</category>
      <category>rails</category>
      <category>postgres</category>
    </item>
    <item>
      <title>How we sped up our rails migration setup in 90%</title>
      <dc:creator>Augusto Pagnossim Frigo</dc:creator>
      <pubDate>Wed, 03 Sep 2025 21:03:43 +0000</pubDate>
      <link>https://dev.to/v360/how-we-sped-up-our-rails-migration-setup-in-90-8be</link>
      <guid>https://dev.to/v360/how-we-sped-up-our-rails-migration-setup-in-90-8be</guid>
      <description>&lt;p&gt;&lt;em&gt;This post is also available &lt;a href="https://dev.to/v360/como-aceleramos-em-90-a-execucao-das-nossas-migracoes-em-rails-1kdn"&gt;in Portuguese&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When using rails, everyone had to create, delete or rollback migrations, as they're a useful resource for structuring your database when you build your application. However, migrations suffer from a problem in terms of scalability: when you add more migrations to your project, they must be run to reflect the database changes in your schema. Moreover, if you switch between branches and run some migrations, your schema.rb may have garbage from other branches, which may force you to rebuild your entire database. This process is not particularly problematic when you have a small project, but in our case, with 6 years of migrations, rebuilding the database was taking a lot of time. Also, as we use a lot of databases that have the same schema, creating new ones was becoming slow, which caused our process of creating new environments to be more painful than it should.&lt;/p&gt;

&lt;p&gt;When we think about solutions to this problem, the first thought may be that maybe it's possible to optimize the migration code in some way or that an easy solution may not exist. In parts, optimizing the migrations' code could help, but it wouldn't be able to speed it up significantly.&lt;/p&gt;

&lt;p&gt;First of all, we need to consider that, by default, every migration in rails creates a new transaction. It makes sense. You don't want to deal with half migration run in your productive system. However, this approach creates performance problems, as having to execute migrations sequentially with a different transaction for every single one of them may become really slow with the amount of migrations that you have. In our case, we need to ship new environments frequently, and those new environments have to be migrated and seeded from zero. Moreover, a lot of times developers may switch from one branch to another and they have to rebuild the entire database if they don't want to commit garbage in the schema.rb.&lt;/p&gt;

&lt;p&gt;Second, our migrations, particularly, had a lot of code that was there for historical reasons. There was lots of code like &lt;code&gt;User.update_all(some_condition: true)&lt;/code&gt; that, once run in the productive environments that existed at the time of the migration, had no reason to be there anymore.&lt;/p&gt;

&lt;p&gt;We noticed that, even though our migrations were significatively slow, our schema:load was fast, lots of times faster than our migration process. However, the schema:load task was not helpful to run in productive environments. schema:load is not capable of creating views, nor executing Ruby code present in migrations. For example, if we had a SQL code creating a view, it wouldn't be created in schema:load. Moreover, some migrations were executing Ruby code to create records (&lt;code&gt;User.create!(name: 'John')&lt;/code&gt;, for instance), and it'd be risky to skip those creations without mapping if they were present in the seeds. Besides the transaction question, when we run a migration, rails adds its timestamp to the table &lt;code&gt;schema_migrations&lt;/code&gt;. To check if a migration was previously executed or not, rails will get the timestamp of the migration being considered to run, and then try to find the same timestamp in this table. If rails is able to find the timestamp, then the migration is skipped. Otherwise, it will be executed. Notice that rails will check the timestamp of a migration against the contents of the table, but the inverse does not occur. As a conclusion, there is no problem in this table having entries that do not exist in the &lt;code&gt;migrate&lt;/code&gt; folder, and we can add a timestamp to this table if we don't want to run a migration in a specific environment.&lt;/p&gt;

&lt;p&gt;Our challenge was to find a way to load the schema and still be able to create those records that only migrations were capable of.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Warning: keep in mind that this solution will remove the advantage of being database-agnostic that schema.rb brings. When you generate the .sql file, you're essentially generating a .sql file that works for your current database adapter, not necessarily for other ones. Also, this solution may not be recommended if you don't have a big problem with migrations being slow, especially because rails doesn't support this way of speeding your migrations up, and keeping the changes history and reversibility may be better in most situations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Immediately, the first thing that came to our minds was to create a .sql file that does the same as the migrations and then load it. We then thought about using &lt;code&gt;structure.sql&lt;/code&gt; as a solution to our problem, as schema.rb is not capable of reflecting creation of views, but structure.sql does. We also needed to place a limit on which migrations would be transformed into plain sql, because we wanted to be able to easily revert the recent ones if needed, and we wanted to guarantee that all the environments we had had already run the migrations that were going to be transformed into plain sql. As we noticed that all of our environments had already run all 2024's migrations, we decided to keep every migration until 2024 as a baseline. Every migration from 2025 onwards would not be converted into plain SQL and would still run normally.&lt;/p&gt;

&lt;p&gt;However, this solution had a problem: structure.sql doesn't reflect created or updated records during migrations. Even though it's expected that the developers always create a copy of updates/creates on seed.rb when they are necessary in new environments, we had no guarantee that every create/update that should be ported to new environments was reflected in the seeds file. We then needed to track which migrations had this problem, but we had (literally) thousands of migrations and reading them one by one was simply not viable and very error-prone. Considering this, we needed to automatically track which migrations insert or update a record, so we could at least exclude from our search all the migrations that didn't create any record, which was most of them. We then created an initializer that would only run in the migration process, and it logs which migrations create any operation of this type. Please notice that this initializer is AI generated and is not suited for best coding practices, but it was used once and not kept in our codebase, so it's fine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config/initializers/migration_data_logger.rb

if defined?(ActiveRecord::Migration)
  module MigrationDataLogger
    class &amp;lt;&amp;lt; self
      attr_accessor :enabled

      def start_logging
        @enabled = true
        @log_file = File.open(Rails.root.join('log/migration_data_changes.log'), 'a')
        @current_migration = nil
        puts 'Migration logging started - writing to log/migration_data_changes.log'
      end

      def stop_logging
        @enabled = false
        @log_file&amp;amp;.close
        puts 'Migration logging stopped'
      end

      def log(message)
        return unless @enabled

        @log_file.puts "[#{Time.current}] #{@current_migration}: #{message}"
        @log_file.flush
      end

      def set_migration(migration)
        @current_migration = "#{migration.version} - #{migration.name}"
      end
    end
  end

  ActiveSupport::Notifications.subscribe('sql.active_record') do |_, started, finished, _, payload|
    MigrationDataLogger.log("#{payload[:sql]}") if MigrationDataLogger.enabled &amp;amp;&amp;amp; payload[:sql] =~ /^(INSERT|UPDATE|DELETE)/i
  end

  ActiveRecord::Migration.prepend(Module.new do
    def migrate(direction)
      MigrationDataLogger.start_logging
      MigrationDataLogger.set_migration(self)
      super
      MigrationDataLogger.stop_logging
    end
  end)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having these migration names in hand, we reduced thousands that we'd need to analyze to just 70. It was a good number to analyze manually, so we didn't go further with the script, but a more automated way of getting the migrations would be to find some way of getting all the executed sql and its return, and discard the ones that affected zero rows. After this, we moved all the code that was in migrations but acted as a seed to db/seeds.rb, and we could finally get rid of the old migrations without having to pay attention to the records that were created.&lt;/p&gt;

&lt;p&gt;After successfully mapping what should be moved to the seeds, we started our process of getting the pre-2025 SQL file. The first step was to add the following line to &lt;code&gt;application.rb&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;config.active_record.schema_format = :sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then moved all post-2024 migrations to another folder (so they're not run in the migrations), and rebuilt the database. This way, we got a SQL file that reflected all pre-2025 migrations and could be easily loaded. This new file, called structure.sql, was then renamed to structure_baseline.sql.&lt;/p&gt;

&lt;p&gt;After doing this, we deleted all pre-2025 migrations and got all post-2024 ones back to our &lt;code&gt;migrate&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;We then manually generated a migration file with 2024 timestamp that would be run post our last 2024 migration. For example, if our last migration had the 20241230xxxx timestamp, we'd generate 20241231xxxx timestamp. We then inserted its timestamp in all our existing environments by directly editing the &lt;code&gt;schema_migrations&lt;/code&gt; table. This way, we could have one migration that loads all the content of the database, but is never executed in environments that already exist. Remember that, for it to work, you have to guarantee that all your environments have already run all the migrations that are being transformed into plain sql.&lt;/p&gt;

&lt;p&gt;With these preparations handled, we created the following migration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class LoadBaselineSchema &amp;lt; ActiveRecord::Migration[7.0]
  disable_ddl_transaction!

  def change
    ActiveRecord::Base.connection.execute(File.read(Rails.root.join('db/structure_baseline.sql')))
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Warning: please notice that the &lt;code&gt;disable_ddl_transaction!&lt;/code&gt; makes the migration run without a transaction in the database and it may be a problem depending on your context. You'll probably want to remove this line of your code (or at least change it to &lt;code&gt;disable_ddl_transaction! if Rails.env.local?&lt;/code&gt;). We chose to use it because it significantly speeds up the .sql loading speed and it's only going to be run in new environments. If some problem occurs in the process of loading the .sql file, we can just recreate the database, since it's a new environment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After doing this, we then removed the application.rb's added line, since changing the final schema.rb to structure.sql was going to be held in another pull request so we are able to review the changes properly. You can explicitly tell application.rb that you want to use ruby instead of sql if you want to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config.active_record.schema_format = :ruby
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then wanted to run the migrations so we could regenerate the &lt;code&gt;schema.rb&lt;/code&gt;. However, when rails tried to load the &lt;code&gt;structure_baseline.sql&lt;/code&gt;, it was trying to recreate the default tables from rails - &lt;code&gt;ar_internal_metadata&lt;/code&gt; and &lt;code&gt;schema_migrations&lt;/code&gt;. We then manually modified the &lt;code&gt;structure.sql&lt;/code&gt; lines &lt;code&gt;CREATE TABLE public.ar_internal_metadata&lt;/code&gt; and &lt;code&gt;CREATE TABLE public.schema_migrations&lt;/code&gt; to include an &lt;code&gt;IF NOT EXISTS&lt;/code&gt; clause.&lt;br&gt;
We also had problems with the indexes of these tables, and we added conditions to only add the indexes if they don't exist.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ALTER TABLE ONLY public.ar_internal_metadata
    ADD CONSTRAINT ar_internal_metadata_pkey PRIMARY KEY (key);
ALTER TABLE ONLY public.schema_migrations
    ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;became&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM pg_constraint
        WHERE conname = 'ar_internal_metadata_pkey'
    ) THEN
        ALTER TABLE ONLY public.ar_internal_metadata
            ADD CONSTRAINT ar_internal_metadata_pkey PRIMARY KEY (key);
    END IF;
END
$$;

DO $$
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM pg_constraint
        WHERE conname = 'schema_migrations_pkey'
    ) THEN
        ALTER TABLE ONLY public.schema_migrations
            ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version);
    END IF;
END
$$;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deleting these table creations and index adding may be a better solution as rails automatically creates them, but we didn't test if it works. From now on, the db:migrate task should work.&lt;/p&gt;

&lt;p&gt;You may want to delete the line &lt;code&gt;COMMENT ON EXTENSION pg_trgm IS 'text similarity measurement and index searching based on trigrams';&lt;/code&gt; too if you're running with a user that does not have privilege to comment on the extension.&lt;/p&gt;

&lt;p&gt;With these modifications, our &lt;code&gt;db:migrate&lt;/code&gt; task was significantly sped up, showing results around 90% optimization.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ruby</category>
      <category>rails</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Mantendo seus dados segregados por Padrão</title>
      <dc:creator>Victor Campos</dc:creator>
      <pubDate>Tue, 07 May 2024 21:14:46 +0000</pubDate>
      <link>https://dev.to/v360/mantendo-seus-dados-segregados-por-padrao-3859</link>
      <guid>https://dev.to/v360/mantendo-seus-dados-segregados-por-padrao-3859</guid>
      <description>&lt;p&gt;&lt;strong&gt;Contexto&lt;/strong&gt;:  Imagina que você teve uma ideia genial, criar um microblog empresarial, onde cada empresa vai poder ter seus colaboradores postando besteira e criando flamewar somente entre eles.&lt;/p&gt;

&lt;p&gt;Você bateu na porta de 2 clientes diferentes, clientes grandes e eles adoraram a ideia, mas trouxeram para você a preocupação de algum colaborador postar algo confidencial e essa informação vazar.&lt;/p&gt;

&lt;p&gt;Também pediram, para caso o contrato acabe, você garanta que deletou TODOS os dados deles.&lt;/p&gt;

&lt;p&gt;Você prometeu para esses 2 clientes que isso seria possível e agora só precisa entregar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Arquitetura de MultiTenancy e Segurança:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;O jeito tradicional de criar multi tenancy, adicionando uma coluna account_id traz junto consigo uma complexidade em segurança e desempenho, como garantir que estou colocando account_id em todas as tabelas que eu realmente preciso? Como garantir que um Dev não esqueceu de adicionar essa coluna nos wheres necessários ? Como garantir índice correto nessa colunas para as queries não ficarem extremamente lentas? &lt;/p&gt;

&lt;p&gt;No ultimo Rails World, tivemos 2 talks dedicadas a esses problemas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Mantendo os dados dos seus clientes separados: &lt;a href="https://www.youtube.com/watch?v=5MLT-QP4S74&amp;amp;list=PLHFP2OPUpCeY9IX3Ht727dwu5ZJ2BBbZP&amp;amp;index=17" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=5MLT-QP4S74&amp;amp;list=PLHFP2OPUpCeY9IX3Ht727dwu5ZJ2BBbZP&amp;amp;index=17&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implementando índices compostos no Rails: &lt;a href="https://www.youtube.com/watch?v=aOD0bZfQvoY&amp;amp;list=PLHFP2OPUpCeY9IX3Ht727dwu5ZJ2BBbZP&amp;amp;index=9" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=aOD0bZfQvoY&amp;amp;list=PLHFP2OPUpCeY9IX3Ht727dwu5ZJ2BBbZP&amp;amp;index=9&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;O próprio DHH tem um artigo falando que Multi Tenancy é o que torna aplicações web difíceis: &lt;a href="https://world.hey.com/dhh/multi-tenancy-is-what-s-hard-about-scaling-web-services-dd1e0e81" rel="noopener noreferrer"&gt;https://world.hey.com/dhh/multi-tenancy-is-what-s-hard-about-scaling-web-services-dd1e0e81&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; Mas, e se fosse possível programar sua aplicação como se ela não fosse Multi Tenancy, se você não tivesse que se preocupar com todas essas questões? &lt;/p&gt;

&lt;p&gt;Simples: &lt;strong&gt;Vamos ter 1 banco de dados para cada cliente.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sim, você leu certo, a ideia por trás de ter segurança por padrão é que cada cliente seu tenha seu próprio banco de dados.&lt;/p&gt;

&lt;p&gt;Com isso seu desenvolvedor não tem como simplesmente esquecer um filtro e disponibilizar os dados de um cliente para o outro.&lt;/p&gt;

&lt;p&gt;Você também não tem que se preocupar em particionar o seu banco em vários shards separados e garantir indicies complicados quando uma tabela estiver completamente lotada. &lt;/p&gt;

&lt;p&gt;Escalar o seu banco de dados? Bom, você vai conseguir escalar verticalmente normalmente, todos os bancos podem viver dentro do mesmo servidor &lt;strong&gt;sem aumentar custos&lt;/strong&gt;, mas também pode escalar horizontalmente se for o caso.&lt;/p&gt;

&lt;p&gt;Imagina que você tem um cliente muito, muito grande e ele sozinho deixa os outros clientes mais lentos? Simples, cria um servidor de banco só para ele, não importa, sua aplicação não precisa saber disso.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E o melhor, o Rails já te da hoje todas as ferramentas que você precisa:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;——&lt;/p&gt;

&lt;p&gt;1) Vamos iniciar o nossa aplicação genial com o bom e velho rails new&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails new enterprise_blog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Vamos gerar o nosso modelo com scafffold para facilitar a vida&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails g scaffold post body:text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) Agora vem a magia, vamos usar a funcionalidade que o rails já dá para definir múltiplos databases:&lt;/p&gt;

&lt;p&gt;Vamos separar as nossas migrações em 2, vamos ter os modelos da nossa aplicação e um modelo tenant que vai controlar os nossos diferentes clientes, esse modelo vai ficar um banco específico para ele, com migrações específicas para ele. O migrations_path diz para o rails que as migrações para esse banco ficam em uma pasta separada.&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;development&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tenancy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;storage/tenancy.sqlite3&lt;/span&gt;
    &lt;span class="na"&gt;migrations_paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db/tenancy&lt;/span&gt;
  &lt;span class="na"&gt;localhost&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;storage/localhost.sqlite3&lt;/span&gt;
  &lt;span class="na"&gt;localhost_2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;storage/localhost_2.sqlite3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4) Agora vamos criar as nossas tabelas principais&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pronto, nesse momento você vai reparar que o rails vai rodar a migração de criação da tabela de posts para os 2 bancos (lindo não?)&lt;/p&gt;

&lt;p&gt;5) Agora, vamos criar o nosso modelo tenant, que vai ter a configuração do banco de dados e o domínio&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails g model tenant name:string custom_domain:string --database tenancy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apontando que ele fica no —database tenancy o rails já cria a migração no lugar certo&lt;/p&gt;

&lt;p&gt;6) Rodando a migração novamente, vamos ver que somente um tenancy foi criado&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;7) Agora vamos criar os dados dos nossos grandes clientes, o localhost e o seu grande concorrente locahost_2&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails c
=&amp;gt; Tenant.create(name: 'localhost', custom_domain: 'localhost')
=&amp;gt; Tenant.create(name: 'localhost_2', custom_domain: '127.0.0.1')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;8) Agora, queremos sempre que a gente acessar localhost os dados do cliente localhost sejam mostrados, e quando a gente acessar o 127.0.0.1, os dados do localhost_2 sejam mostrados, para isso, vamos adicionar um middleware, um código que roda antes de cada requisição no rails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/middleware/tenant_selector.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TenantSelector&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&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;tenant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Tenant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tenant_from_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;switch&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="c1"&gt;# aqui vem a magia, a requisição vai continuar dentro do tenant, não vai ser possível acessar o banco de dados de outros clientes&lt;/span&gt;
        &lt;span class="vi"&gt;@app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'text/html'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Not Found'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;9) Agora um pouco de configuração para fazer o rails usar o nosso middleware e fazer o ActiveRecord::Base saber se conectar com cada banco configurado&lt;/p&gt;

&lt;p&gt;No application.rb vamos adicionar&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s1"&gt;'../lib/middleware/tenant_selector'&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;EnterpriseBlog&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt; &lt;span class="no"&gt;TenantSelector&lt;/span&gt; &lt;span class="c1"&gt;# Se usar o Devise `config.middleware.insert_before Warden::Manager, TenantSelector`&lt;/span&gt;

    &lt;span class="c1"&gt;# aqui vem o segredo, para cada banco no database.yml, vamos criar um shard e o nome desse shard vai ser igual ao nome do tenant&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;after_initialize&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connects_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;shards: &lt;/span&gt;&lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configurations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configs_for&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env_name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'tenancy'&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_sym&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;writing: &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_sym&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;10) E por fim, vamos implementar o nosso método de busca e switch do tenant&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tenant&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;TenancyRecord&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tenant_from_env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Tenant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;custom_domain: &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'SERVER_NAME'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c1"&gt;# Aqui recomendo colocar um cache em memória por questões de desempenho&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;switch&lt;/span&gt;
    &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connected_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;role: :writing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;shard: &lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_sym&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="k"&gt;yield&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;11) Pronto,&lt;br&gt;
Agora ao acessar o localhost ou 127.0.0.1, você vai ver posts diferentes, com menos de 20 linhas de código.&lt;/p&gt;

&lt;p&gt;——————————&lt;/p&gt;

&lt;p&gt;Obvio, nem tudo são flores, vou colocar aqui alguns tradeoffs&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Atualmente aqui na empresa temos algumas centenas de banco de dados, não tem se mostrado um problema de escala para o rails/postgres gerenciar todas essas conexões, mas não sei se essa arquitetura escala para milhares de clientes, quando eu tiver esse problema eu trago aqui a solução&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No nosso contexto, não temos uma feature que o cliente faz um signup e isso gera um database novo, o processo de assinatura de contrato demora alguns dias, o que é tempo suficiente para o nosso time de infra alterar o database.yml e fazer um deploy, temos planos para automatizar essa etapa, mas provavelmente não esse ano (se você pensar como fazer antes, só avisar)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Agregar os dados de todos os clientes para ter métricas gerenciais é um pouco trick e deu trabalho para o nosso time de dados, posso escrever mais sobre isso no futuro&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Provavelmente você vai querer complicar um pouco e ter um current attribute para o tenant e usar ele como namespace do seu cache &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;config.cache_store = :redis_cache_store, { url: ENV['REDIS_CACHE_STORE_URL'], namespace: proc { Current.tenant&amp;amp;.id } }&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Você vai precisar fazer a mesma lógica do middleware no sidekiq, por sorte, o sidekiq também da suporte a middlewares&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Isso não resolve variáveis globais, cuidado com elas &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Provavelmente você vai ter que configurar um pgbouncer antes do necessário para gerenciar as conexões com o banco&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Gerar um dump do schema para o rails não ter que ir verificar para cada banco que você tem: &lt;a href="https://stackoverflow.com/questions/38778689/how-to-fix-a-slow-implicit-query-on-pg-attribute-table-in-rails" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/38778689/how-to-fix-a-slow-implicit-query-on-pg-attribute-table-in-rails&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Mas, depois de 3 anos usando essa arquitetura, não temos o que reclamar, conseguimos no nosso dia a dia programar sem se preocupar com isso, no nosso nível de escala não temos problema de desempenho por conta da arquitetura e com certeza não temos as dores de cabeça de ter um tenant_id espalhado por centenas de tabelas no nosso banco de dados&lt;/p&gt;

</description>
      <category>database</category>
      <category>rails</category>
      <category>multitenancy</category>
      <category>security</category>
    </item>
    <item>
      <title>Resumo: "Muito Além da Sorte"</title>
      <dc:creator>Victor Campos</dc:creator>
      <pubDate>Thu, 07 Apr 2022 15:03:36 +0000</pubDate>
      <link>https://dev.to/v360/resumo-muito-alem-da-sorte-kfk</link>
      <guid>https://dev.to/v360/resumo-muito-alem-da-sorte-kfk</guid>
      <description>&lt;p&gt;&lt;strong&gt;Muito Além da Sorte&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Aqui na V360 buscamos criar um produto que realmente gere valor para o cliente, o que importa não é o número de feature ou o número de linhas que uma pessoa desenvolvedora digitou, mas sim o valor final gerado.&lt;/p&gt;

&lt;p&gt;Encontrar o lugar certo onde botar os esforços não é uma tarefa fácil, longe disso. Na verdade, a maioria das empresas falham exatamente nesse ponto. Sabem fazer, mas não sabem "o que fazer".&lt;/p&gt;

&lt;p&gt;Para nos ajudar nessa jornada, fomos buscar um pouco do conhecimento do Clayton M. Christensen, pai da Inovação Disruptiva, em seu excelente livro "Muito Além da Sorte".&lt;/p&gt;

&lt;p&gt;Segue um resumo pessoal que fiz da leitura, compartilhado com toda a empresa e agora com vocês:&lt;/p&gt;




&lt;p&gt;A maioria das empresas quando tenta inovar busca correlação e não causalidade &lt;/p&gt;

&lt;p&gt;Uma pergunta melhor que se deve fazer é: Porque contratar esse produto?&lt;/p&gt;

&lt;p&gt;Os clientes compram/usam algum produto pois eles tem um &lt;br&gt;
“job to be done”. &lt;/p&gt;




&lt;p&gt;Empresas criam processos eficientes para o que querem atingir, mas se estiverem fazendo as perguntas erradas sobre inovação, vão ser eficientes em responder as perguntas erradas.&lt;/p&gt;

&lt;p&gt;No seu livro de maior sucesso, o dilema do inovador (ótimo livro por sinal), o autor explica porque as empresas caem perante a uma inovação disruptiva, mas não conta quais paços dar para gerá-la. Essa é a intenção desse “novo” livro.&lt;/p&gt;

&lt;p&gt;Entender a razão que o produto foi contratado ajuda a entender os seus competidores. O livro mostra o estudo de caso de um fast food de milkshake:&lt;/p&gt;

&lt;p&gt;Antes das 9 da manhã, os clientes contratam o milkshake para resolver o trabalho de tomar café da manhã enquanto se deslocam para o trabalho. Os competidores são uma banana, sanduíche ou mesmo doces.&lt;/p&gt;

&lt;p&gt;Já no final da tarde, quando um pai entra com seu filho e compra um milkshake, este está competindo com todos os “nãos” que o pai deu durante a semana, e é o momento para parecer um “pai legal”.&lt;/p&gt;

&lt;p&gt;Além de entender os competidores, analisar o trabalho que se quer fazer também leva a compreender quais características são importantes. &lt;/p&gt;

&lt;p&gt;Se seu público alvo são os consumidores da manhã, um milkshake que cause entupimento no canudo pode ser um problema, dado que as pessoas estão dirigindo enquanto tomam. Já para o pai que quer comprar o amor do seu filho com o milkshake, mas não quer se sentir tão culpado, ter uma opção pequena ajuda.&lt;/p&gt;

&lt;p&gt;Para inovar, precisamos escrever um currículo tanto para o nosso produto quanto para os concorrentes. Assim sabemos para qual direção queremos seguir e quais “job to be done” queremos atacar primeiro.&lt;/p&gt;

&lt;p&gt;O market share deveria ser calculado por "job to be done" e não pela categoria do produto.&lt;/p&gt;

&lt;p&gt;O autor cita o exemplo da margarina, onde a Unilever tinha 70% do mercado em 1997 e mesmo assim a divisão dela não durou 20 anos, pois perdeu espaço para o azeite e a manteiga. E mesmo no final da divisão da margarina dentro da empresa, a empresa continuou sendo líder de mercado desse produto.&lt;/p&gt;




&lt;p&gt;Exemplo que não está no livro. A Netflix considera que jogos são um concorrente tão grande quanto streaming, e ano que vem vão entrar nesse mercado.&lt;/p&gt;

&lt;p&gt;2019&lt;br&gt;
&lt;a href="https://www.uol.com.br/start/ultimas-noticias/2019/01/18/como-assim-para-a-netflix-fortnite-e-seu-principal-concorrente.htm"&gt;https://www.uol.com.br/start/ultimas-noticias/2019/01/18/como-assim-para-a-netflix-fortnite-e-seu-principal-concorrente.htm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2021&lt;br&gt;
&lt;a href="https://www.terra.com.br/gameon/netflix-confirma-que-vai-adicionar-jogos-a-assinatura-sem-custo-adicional,2b0e30deb7138e9367619df764987e40voijx80m.html"&gt;https://www.terra.com.br/gameon/netflix-confirma-que-vai-adicionar-jogos-a-assinatura-sem-custo-adicional,2b0e30deb7138e9367619df764987e40voijx80m.html&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;A definição de “job”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A chave principal para entender a teoria é a que os consumidores não compram um produto, eles colocam os mesmos na sua vida para fazer progresso.&lt;/p&gt;

&lt;p&gt;Descobrir qual o progresso que o cliente quer ter ao “contratar” o produto é a chave para entender as escolhas dele.&lt;/p&gt;




&lt;p&gt;O segundo fator principal é a circunstância, pois sem interpretar a circunstância que o cliente tomou uma decisão, não conseguimos entender o progresso que ele quer fazer.&lt;/p&gt;




&lt;p&gt;E por último, compreender a complexidade emocional e social do cliente.&lt;/p&gt;




&lt;p&gt;Entender essas três dimensões proporciona o “job to be done”. &lt;/p&gt;

&lt;p&gt;Mais importante do “quem” ou “o que” é “o porque”.&lt;/p&gt;




&lt;p&gt;Onde procurar os "job to be done"?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Na sua própria vida&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vários negócios começam por dores dos próprios criadores, e entender a fundo essas dores pode ser um caminho. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Competindo contra o nada&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Muitas vezes seu maior competidor é não fazer nada. No livro o autor da exemplo de uma universidade EAD que viu que muitos adultos americanos preferiam ficar em casa sem fazer nada do que estudar, e foram atrás para entender o motivo. Com isso, criaram uma das maiores universidades americanas de EAD.&lt;/p&gt;

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

&lt;p&gt;Identificar onde os consumidores estão usando um produto com a função de A para realizar porcamente a função de B, por não ter nenhuma alternativa melhor.&lt;/p&gt;

&lt;p&gt;A empresa Intuit percebeu que as pessoas usavam o software de gestão de gastos pessoais dela para gerir a conta de pequenas empresas, ao invés de usar softwares completos de contabilidade por acharem complexo demais.&lt;/p&gt;

&lt;p&gt;Ao perceber isso criaram o quickbook, o produto que mais cresce na empresa.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Buscar por coisas que as pessoas não querem fazer &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Descobrir atividades que as pessoas tem que fazer mas não querem.&lt;br&gt;
Acho que o V360 se aplica aqui.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Casos não usuais&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Entender porque certos consumidores usam seu produto de uma maneira que não deveria ser usada.&lt;/p&gt;

&lt;p&gt;Confesso que achei repetido com o caso 3.&lt;/p&gt;




&lt;p&gt;Após identificar o “job to be done” usando uma das técnicas acima, é necessário entender o contexto social, econômico e emocional.&lt;/p&gt;




&lt;p&gt;Existem dois momentos onde o consumidor contrata um produto: o “Big Hire”, que é exatamente o momento em que ele paga, e o “Little Hire”, quando o cliente usa o produto.&lt;/p&gt;

&lt;p&gt;Medir os dois momentos é de extrema importância&lt;/p&gt;




&lt;p&gt;Existem quatro forças que competem entre si na hora do consumidor contratar um produto.&lt;/p&gt;

&lt;p&gt;Duas levam o consumidor a contratar algo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;O tamanho do problema &lt;/li&gt;
&lt;li&gt;O quão boa é a solução &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Outras duas levam o consumidor a ficar com o que já conhece:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A inércia da mudança &lt;/li&gt;
&lt;li&gt;A ansiedade da mudança&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Muitas empresas focam na primeira lista,  melhorando seus produtos e identificando grandes dores, quando deveriam também tentar reduzir o impacto da segunda.&lt;/p&gt;

&lt;p&gt;Somente quando a soma das duas primeiras forças se sobressai muito a soma da segunda, que a contratação do produto ocorre.&lt;/p&gt;




&lt;p&gt;Consumidores não são bons em dizer o que eles realmente querem, mas podem dizer quais são suas dores.&lt;/p&gt;




&lt;p&gt;Para conseguir cobrar um valor acima do mercado você precisa entender o “job to be done”, criar uma experiência única e incorporar essa experiência em todo o seu processo.&lt;/p&gt;

&lt;p&gt;O exemplo do livro é a Ikea, uma loja de móveis.&lt;/p&gt;

&lt;p&gt;Nessa loja, os móveis são pensados para serem montados pelo próprio consumidor, de uma forma que cabe em qualquer carro e com uma única ferramenta que já vem no pacote. Além disso, a Ikea possui um lugar para as crianças ficarem brincando e um café para o cliente se recompensar “pelo dia chato de comprar móveis”.&lt;/p&gt;

&lt;p&gt;O produto da Ikea é fácil de copiar, mas toda a experiência não é.&lt;/p&gt;




&lt;p&gt;Os processos da sua empresa devem ter o "job to be done" como centro.&lt;/p&gt;

&lt;p&gt;As melhorias de processo tem que acontecer para você ficar mais eficiente em resolver o “job to be done”, pois não adianta ficar mais eficiente em fazer a coisa errada.&lt;/p&gt;




&lt;p&gt;A falácia que as empresas acabam caindo com o crescimento:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A falácia dos dados operacionais &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As empresas acreditam que é possível migrar de análises de poucos casos a fundo quando são pequenas, para análise de muitos casos usando estatística quando são grandes._&lt;/p&gt;

&lt;p&gt;Dados só são úteis se a empresa realmente entender o contexto por trás dos mesmos.&lt;/p&gt;

&lt;p&gt;Pois no geral, a empresa tem os dados de quantas furadeiras vendeu, mas não de quantos buracos o cliente fez com elas.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A falácia do crescimento dentro da própria base&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esse é um ponto que me fez refletir bastante. Confesso que não sei se tenho total conhecimento, mesmo relendo essa parte algumas vezes.&lt;/p&gt;

&lt;p&gt;O autor argumenta que muitas empresas buscam aumentar receita vendendo outros produtos para o mesmo cliente, mas que isso faz com que ela perca a visão de “job to be done” que a levou até ali.&lt;/p&gt;

&lt;p&gt;Um exemplo do próprio New York Times, que tenta atacar vários “job to be done” com informações muito ricas em conteúdo e ao mesmo tempo ser um jornal para passar o tempo. E com isso perde espaço para jornais de metrô que são de graça e para jornais especializados que são mais caros que ele, como o The Economist.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A falácia dos dados de confirmação &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se você está procurando confirmar um ponto de vista nos dados, eles vão dizer o que você quer ouvir.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Resumo: Creative Selection</title>
      <dc:creator>Izaias Miguel</dc:creator>
      <pubDate>Mon, 21 Mar 2022 13:27:06 +0000</pubDate>
      <link>https://dev.to/v360/resumo-creative-selection-3moj</link>
      <guid>https://dev.to/v360/resumo-creative-selection-3moj</guid>
      <description>&lt;p&gt;Esse livro estava em minha estante há uns 2 anos e ainda não tinha conseguido priorizar. Finalmente consegui ler e achei bem interessante, com alguns pontos a compartilhar.&lt;/p&gt;

&lt;p&gt;O livro basicamente conta algumas estórias bem bacanas dos bastidores da criação de novos produtos pela Apple. O autor foi engenheiro de software lá entre 2001 e 2017, passando ainda pelo AirBnB depois disso. &lt;/p&gt;

&lt;p&gt;Por ter sido escrito por alguém que trabalhou no código de alguns dos principais produtos da Apple no período, o livro traz uma visão mais técnica que me chamou bastante atenção (dada minha formação em Ciência da Computação). Tem até um trecho de código em C++, rs.&lt;/p&gt;

&lt;p&gt;O autor, Ken Kocienda, foi um dos responsáveis pela criação do Safari, do envio e visualização de e-mails em HTML pelo Mail, e pela criação do teclado do iPhone (e mais tarde do iPad). Portanto, produtos essenciais no ecossistema da Apple. &lt;/p&gt;

&lt;p&gt;O Safari foi lançado em 2003 apenas - antes dele, poucas pessoas se lembram, mas o browser do Mac era o Internet Explorer da Microsoft. O teclado era um dos principais riscos para o lançamento do iPhone, pois uma das principais inovações à época era justamente ser um telefone sem teclado, concorrendo com o famoso teclado do Blackberry (além do fato de que o input de dados era um dos principais traumas da Apple em dispositivos móveis, dado que tinha sido um dos principais fatores de insucesso do Newton na década de 90).&lt;/p&gt;

&lt;p&gt;Em resumo, o autor cita como principais fatores de sucesso na criação dos produtos da Apple: a cultura da empresa, a estrutura dos times e o processo de inovação.&lt;/p&gt;

&lt;p&gt;Sobre a cultura, ele cita sete elementos essenciais para o sucesso dos softwares da Apple, dando exemplos desses pontos ao longo das estórias, que conta:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inspiração: pensar em grandes ideias e imaginar o que poderia ser possível&lt;/li&gt;
&lt;li&gt;Colaboração: trabalhar em conjunto com outras pessoas e buscar combinar suas forças complementares&lt;/li&gt;
&lt;li&gt;Habilidades (craft): aplicar habilidades para atingir resultados de alta qualidade e sempre tentar fazer melhor&lt;/li&gt;
&lt;li&gt;Diligência: fazer o trabalho chato necessário e nunca recorrer a atalhos ou meias medidas&lt;/li&gt;
&lt;li&gt;Agilidade na tomada e decisão (decisiveness): fazer escolhas difíceis e se recusar a atrasar ou procrastinar&lt;/li&gt;
&lt;li&gt;Gosto (taste): desenvolver um senso refinado de julgamento e achar o equilíbrio que produz um todo prazeroso e integrado&lt;/li&gt;
&lt;li&gt;Empatia: tentar ver o mundo na perspectiva de outras pessoas e criar um trabalho que se encaixe em suas vidas e se adapte à suas necessidades&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esses princípios não estavam escritos em lugar nenhum, nem eram passados aos recém-contratados. Mas, fluíam do trabalho tanto top down, a partir da liderança do Steve Jobs, quanto bottom-up, a partir dos engenheiros.&lt;/p&gt;

&lt;p&gt;Sobre a estrutura dos times, o autor reforça que sempre eram times muito pequenos, onde os próprios engenheiros eram responsáveis pelo produto, da ideação à codificação - não havia um “product manager”. &lt;/p&gt;

&lt;p&gt;Apenas 10 pessoas trabalharam no Safari e apenas 25 foram responsáveis por todas as patentes do iPhone. Eles quebravam o software em diversas pequenas partes que se falavam muito pouco. Por exemplo, o Ken como responsável pelo teclado do iPhone, não sabia absolutamente nada sobre o que estava sendo feito em termos de hardware, só trabalhava com um protótipo da tela conectado a um computador. &lt;/p&gt;

&lt;p&gt;Em relação aos demais softwares do iPhone, a interação de Ken era basicamente fazer com que as pessoas que criavam aplicativos, como Notas e E-mail, usassem o teclado e dessem feedbacks ao longo do uso. Além da compartimentalização em pequenos times, a Apple ainda tinha uma preocupação muito grande com o sigilo, de forma que poucas pessoas tinham acesso ao todo.&lt;/p&gt;

&lt;p&gt;O processo de desenvolvimento é a principal ênfase do livro. O autor cita o processo que ele denominou de “Seleção Criativa”, é basicamente a criação rápida de protótipos que são validados pela alta liderança da empresa, e em alguns momentos pelo próprio Steve Jobs. Esses protótipos são sempre funcionais - eles não “perdem tempo” criando documentos ou protótipos não funcionais, pois segundo ele é muito difícil provar a viabilidade e usabilidade de algo que as pessoas não estão usando. &lt;/p&gt;

&lt;p&gt;Esse ciclo de criação rápida de protótipos funcionais, feedback pela alta liderança e ajustes no protótipo com base nos feedbacks, permite que o time fique focado em entregar código tendo retornos muito rápidos e foque no que é importante para o usuário, o que casa muito com os princípios de agilidade. &lt;/p&gt;

&lt;p&gt;Ken cita ainda a diferença dessa abordagem para a de empresas como o Google, que foca muito em fazer testes A/B para tudo e em lançar produtos não tão maduros ainda para receber feedback dos usuários. Na Apple, os próprios times são responsáveis pela tomada de decisão, dado que a cultura é de lançar produtos já maduros e que praticamente não foram testados com usuários “reais” pelo sigilo que a empresa impõe. Essa diferença é muito ressaltada pelos aspectos culturais de decisiveness e taste, onde os próprios engenheiros tomam decisões o tempo todo pelos usuários da Apple.&lt;/p&gt;

&lt;p&gt;O autor ainda dá ênfase às demonstrações de produtos para Steve Jobs. Nessas demonstrações ele sempre estava com a atenção totalmente focada e pedia a opinião do apresentador sobre o que ele achava de algo (ex: retirar um dos dois templates de teclado do iPad). Steve sempre prezava pela simplicidade que ajudava o usuário a usar e retirava toneladas de complexidade do desenvolvimento. Quando pedia a opinião, era para ensinar as pessoas a pensarem sobre o produto com a mesma cabeça que ele, em vez de só dizer como deveria ser. &lt;/p&gt;

&lt;p&gt;Eram muitas demos, então ele prezava por comunicação direta e concisa, sem rodeios. Além disso, ele sempre dava um foco único para o time - no caso do Safari por exemplo, ele queria que o browser fosse muito mais rápido que os demais, o que fez com que o time focasse totalmente em não adicionar nada que aumentasse o tempo de resposta e trabalhasse arduamente para remover todos os gargalos. &lt;/p&gt;

&lt;p&gt;Em qualquer atividade complexa, comunicar uma visão bem articulada para o que você está tentando fazer é o ponto inicial para descobrir como fazer. Embora criar essa visão seja difícil, é muito mais difícil completar todo o circuito: ter uma ideia, um plano para realizá-la e executar o plano com altos padrões, tudo sem se atolar ou mudar a direção. &lt;/p&gt;

&lt;p&gt;Ter um único objetivo para ser excelente é muito importante para atingir a excelência. Quando foi lançado, o Safari era 3x mais rápido que os demais browsers.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Conclusão *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Achei muito legal tanto para conhecer mais a estória de alguns dos produtos mais icônicos da Apple (depois disso não consigo mais digitar no teclado do iPhone sem pensar de forma um pouco mais profunda sobre o que está por trás) quanto por conhecer o interior da Apple. Para mim, ela sempre foi uma das maiores caixas pretas em termos de estrutura e processo de inovação. Vale muito a leitura para quem se interessa pelo tema.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Resumo: Creative Selection</title>
      <dc:creator>leticiacruzz</dc:creator>
      <pubDate>Mon, 21 Mar 2022 13:23:50 +0000</pubDate>
      <link>https://dev.to/v360/resumo-creative-selection-mno</link>
      <guid>https://dev.to/v360/resumo-creative-selection-mno</guid>
      <description>&lt;p&gt;Esse livro estava em minha estante há uns 2 anos e ainda não tinha conseguido priorizar. Finalmente consegui ler e achei bem interessante, com alguns pontos a compartilhar.&lt;/p&gt;

&lt;p&gt;O livro basicamente conta algumas estórias bem bacanas dos bastidores da criação de novos produtos pela Apple. O autor foi engenheiro de software lá entre 2001 e 2017, passando ainda pelo Airbnb depois disso. &lt;/p&gt;

&lt;p&gt;Por ter sido escrito por alguém que trabalhou no código de alguns dos principais produtos da Apple no período, o livro traz uma visão mais técnica que me chamou bastante atenção (dada minha formação em Ciência da Computação). Tem até um trecho de código em C++, rs.&lt;/p&gt;

&lt;p&gt;O autor, Ken Kocienda, foi um dos responsáveis pela criação do Safari, do envio e visualização de e-mails em HTML pelo Mail, e pela criação do teclado do iPhone (e mais tarde do iPad). Portanto, produtos essenciais no ecossistema da Apple. &lt;/p&gt;

&lt;p&gt;O Safari foi lançado em 2003 apenas - antes dele, poucas pessoas se lembram, mas o browser do Mac era o Internet Explorer da Microsoft. O teclado era um dos principais riscos para o lançamento do iPhone, pois uma das principais inovações à época era justamente ser um telefone sem teclado, concorrendo com o famoso teclado do Blackberry (além do fato de que o input de dados era um dos principais traumas da Apple em dispositivos móveis, dado que tinha sido um dos principais fatores de insucesso do Newton na década de 90).&lt;/p&gt;

&lt;p&gt;Em resumo, o autor cita como principais fatores de sucesso na criação dos produtos da Apple: a cultura da empresa, a estrutura dos times e o processo de inovação.&lt;/p&gt;

&lt;p&gt;Sobre a cultura, ele cita sete elementos essenciais para o sucesso dos softwares da Apple, dando exemplos desses pontos ao longo das estórias, que conta:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inspiração: pensar em grandes ideias e imaginar o que poderia ser possível&lt;/li&gt;
&lt;li&gt;Colaboração: trabalhar em conjunto com outras pessoas e buscar combinar suas forças complementares&lt;/li&gt;
&lt;li&gt;Habilidades (craft): aplicar habilidades para atingir resultados de alta qualidade e sempre tentar fazer melhor&lt;/li&gt;
&lt;li&gt;Diligência: fazer o trabalho chato necessário e nunca recorrer a atalhos ou meias medidas&lt;/li&gt;
&lt;li&gt;Agilidade na tomada e decisão (decisiveness): fazer escolhas difíceis e se recusar a atrasar ou procrastinar&lt;/li&gt;
&lt;li&gt;Gosto (taste): desenvolver um senso refinado de julgamento e achar o equilíbrio que produz um todo prazeroso e integrado&lt;/li&gt;
&lt;li&gt;Empatia: tentar ver o mundo na perspectiva de outras pessoas e criar um trabalho que se encaixe em suas vidas e se adapte à suas necessidades&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esses princípios não estavam escritos em lugar nenhum, nem eram passados aos recém-contratados. Mas, fluíam do trabalho tanto top down, a partir da liderança do Steve Jobs, quanto bottom-up, a partir dos engenheiros.&lt;/p&gt;

&lt;p&gt;Sobre a estrutura dos times, o autor reforça que sempre eram times muito pequenos, onde os próprios engenheiros eram responsáveis pelo produto, da ideação à codificação - não havia um “product manager”. &lt;/p&gt;

&lt;p&gt;Apenas 10 pessoas trabalharam no Safari e apenas 25 foram responsáveis por todas as patentes do iPhone. Eles quebravam o software em diversas pequenas partes que se falavam muito pouco. Por exemplo, o Ken como responsável pelo teclado do iPhone, não sabia absolutamente nada sobre o que estava sendo feito em termos de hardware, só trabalhava com um protótipo da tela conectado a um computador. &lt;/p&gt;

&lt;p&gt;Em relação aos demais softwares do iPhone, a interação de Ken era basicamente fazer com que as pessoas que criavam aplicativos, como Notas e E-mail, usassem o teclado e dessem feedbacks ao longo do uso. Além da compartimentalização em pequenos times, a Apple ainda tinha uma preocupação muito grande com o sigilo, de forma que poucas pessoas tinham acesso ao todo.&lt;/p&gt;

&lt;p&gt;O processo de desenvolvimento é a principal ênfase do livro. O autor cita o processo que ele denominou de “Seleção Criativa”, é basicamente a criação rápida de protótipos que são validados pela alta liderança da empresa, e em alguns momentos pelo próprio Steve Jobs. Esses protótipos são sempre funcionais - eles não “perdem tempo” criando documentos ou protótipos não funcionais, pois segundo ele é muito difícil provar a viabilidade e usabilidade de algo que as pessoas não estão usando. &lt;/p&gt;

&lt;p&gt;Esse ciclo de criação rápida de protótipos funcionais, feedback pela alta liderança e ajustes no protótipo com base nos feedbacks, permite que o time fique focado em entregar código tendo retornos muito rápidos e foque no que é importante para o usuário, o que casa muito com os princípios de agilidade. &lt;/p&gt;

&lt;p&gt;Ken cita ainda a diferença dessa abordagem para a de empresas como o Google, que foca muito em fazer testes A/B para tudo e em lançar produtos não tão maduros ainda para receber feedback dos usuários. Na Apple, os próprios times são responsáveis pela tomada de decisão, dado que a cultura é de lançar produtos já maduros e que praticamente não foram testados com usuários “reais” pelo sigilo que a empresa impõe. Essa diferença é muito ressaltada pelos aspectos culturais de decisiveness e taste, onde os próprios engenheiros tomam decisões o tempo todo pelos usuários da Apple.&lt;/p&gt;

&lt;p&gt;O autor ainda dá ênfase às demonstrações de produtos para Steve Jobs. Nessas demonstrações ele sempre estava com a atenção totalmente focada e pedia a opinião do apresentador sobre o que ele achava de algo (ex: retirar um dos dois templates de teclado do iPad). Steve sempre prezava pela simplicidade que ajudava o usuário a usar e retirava toneladas de complexidade do desenvolvimento. Quando pedia a opinião, era para ensinar as pessoas a pensarem sobre o produto com a mesma cabeça que ele, em vez de só dizer como deveria ser. &lt;/p&gt;

&lt;p&gt;Eram muitas demos, então ele prezava por comunicação direta e concisa, sem rodeios. Além disso, ele sempre dava um foco único para o time - no caso do Safari por exemplo, ele queria que o browser fosse muito mais rápido que os demais, o que fez com que o time focasse totalmente em não adicionar nada que aumentasse o tempo de resposta e trabalhasse arduamente para remover todos os gargalos. &lt;/p&gt;

&lt;p&gt;Em qualquer atividade complexa, comunicar uma visão bem articulada para o que você está tentando fazer é o ponto inicial para descobrir como fazer. Embora criar essa visão seja difícil, é muito mais difícil completar todo o circuito: ter uma ideia, um plano para realizá-la e executar o plano com altos padrões, tudo sem se atolar ou mudar a direção. &lt;/p&gt;

&lt;p&gt;Ter um único objetivo para ser excelente é muito importante para atingir a excelência. Quando foi lançado, o Safari era 3x mais rápido que os demais browsers.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Conclusão *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;O livro é muito legal tanto para conhecer mais a estória de alguns dos produtos mais icônicos da Apple (depois disso não consigo mais digitar no teclado do iPhone sem pensar de forma um pouco mais profunda sobre o que está por trás) quanto para conhecer o interior da Apple. Para mim, a empresa sempre foi uma das maiores caixas pretas em termos de estrutura e processo de inovação. Vale muito a leitura para quem se interessa pelo tema.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Processo de certificação ISO/IEC 27001:2013  x V360</title>
      <dc:creator>Diego Panaro</dc:creator>
      <pubDate>Thu, 03 Mar 2022 19:11:33 +0000</pubDate>
      <link>https://dev.to/v360/processo-de-certificacao-isoiec-270012013-x-v360-3o1d</link>
      <guid>https://dev.to/v360/processo-de-certificacao-isoiec-270012013-x-v360-3o1d</guid>
      <description>&lt;p&gt;Você já deve ter ouvido falar em empresas certificadas ou acreditadas nas melhores práticas de mercado, não é mesmo? São as famosas empresas ISO Certified, certificadas em gestão da qualidade, ambiental, entre outras.&lt;/p&gt;

&lt;p&gt;Mas e no quesito segurança da informação, você conhece alguma empresa certificada? &lt;/p&gt;

&lt;p&gt;De acordo com uma recente matéria publicada na revista Exame, em fevereiro de 2022, no ano passado (2021), cerca de 26% das empresas brasileiras sofreram algum tipo de ataque cibernético, sendo: phishing, vírus e ransomware os mais comuns.&lt;/p&gt;

&lt;p&gt;A ISO/IEC 27001:2013 oferece informações completas de como implementar um Sistema de Gestão de Segurança da Informação (SGSI). Ela tem como objetivo o atendimento de requisitos obrigatórios e processos que reduzem riscos e visam aumentar a segurança das informações da empresa. De acordo com a última pesquisa ISO, divulgada em 2020, apenas 148 empresas no Brasil possuem essa certificação.&lt;/p&gt;

&lt;p&gt;Neste artigo falaremos um pouco sobre o processo da V360 de certificação na ISO/IEC 27001:2013, qual caminho seguimos e os desafios e motivadores nessa jornada.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sobre a ISO/IEC 27001:2013&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A ISO é a Organização Internacional para Normalização, que tem como objetivo fornecer normativas de padronização em quesitos ambientais e de qualidade, por exemplo.&lt;/p&gt;

&lt;p&gt;Para segurança, foi criada a ISO/IEC 27001:2013 para estabelecer, implementar e manter um Sistema de Gestão de Segurança da Informação (SGSI). E o objetivo principal é o atendimento de requisitos, controles e procedimentos para garantir a segurança da informação. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nosso processo de certificação (V360 x ISO 27001)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Agora que você já sabe um pouco mais sobre a ISO 27001, irei contar sobre o nosso processo de certificação. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Passo número 1:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Para iniciar essa caminhada, um ponto fundamental sem dúvida alguma, foi contar com a participação da alta direção. As certificações são processos custosos e irão ocupar parte do orçamento anual da empresa, e uma vez acreditada, serão necessárias auditorias internas e externas para manutenção dos certificados. &lt;/p&gt;

&lt;p&gt;No mercado existem empresas especializadas no processo de certificação. São consultorias que irão verificar o estágio em que sua empresa está no momento e os próximos passos que deverão ser percorridos. &lt;/p&gt;

&lt;p&gt;A próxima etapa será a elaboração de documentos e procedimentos obrigatórios de acordo com a normativa que deverá ser adquirida diretamente no site da ISO. Eles servirão como um guia durante todo o processo de certificação e auditorias. &lt;/p&gt;

&lt;p&gt;Lembre-se, o papel aceita tudo. Empenhe-se ao máximo para que a linguagem e regras utilizadas durante todos os procedimentos sejam aderentes à sua organização. &lt;/p&gt;

&lt;p&gt;Na V360 buscamos a simplicidade e objetividade nas documentações, o que acreditamos ter sido um diferencial para o atingimento do nosso objetivo. &lt;/p&gt;

&lt;p&gt;Após a elaboração dos procedimentos e aprovação interna, será necessária a realização de treinamentos e capacitação do time, sendo esse um dos passos principais para obtenção e manutenção da certificação.&lt;/p&gt;

&lt;p&gt;Todo mundo é responsável pela manutenção da segurança no dia a dia da organização. Sem o envolvimento dos times, com certeza o barco vai naufragar. A dica aqui é trazer especialistas de mercado para meetups e bate papos internos, pois com certeza ajudarão no engajamento do projeto.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Passo número 2:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Com os procedimentos divulgados e bem estruturados, chegou a hora da primeira avaliação, a auditoria interna. Algumas consultorias contam com esse tipo de serviço, o que aconteceu em nosso caso. &lt;/p&gt;

&lt;p&gt;Por aqui, ainda não tínhamos a experiência de passar por uma auditoria, e se for o seu caso, vou te dar algumas dicas que considero essenciais. Antes, é fundamental que você compreenda que serão levantados pontos de melhoria e não conformidades, os mesmos deverão ser tratados até o início de sua auditoria certificadora para ajustes no seu SGSI. &lt;/p&gt;

&lt;p&gt;Agora vamos as dicas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;É normal sentir aquele frio na barriga, mas mantenha-se calmo e focado;&lt;/li&gt;
&lt;li&gt;Em caso de dúvidas sobre o que foi perguntado, peça ao auditor que seja mais claro e qual seção ele se refere, isso pode te ajudar a identificar o documento em questão;&lt;/li&gt;
&lt;li&gt;Anote todos os pontos apresentados como não conformidades e oportunidades de melhoria;&lt;/li&gt;
&lt;li&gt;Você pode e deve consultar seus procedimentos durante a auditoria, sempre que possível mostrando evidências que comprovem que seus documentos estão sendo utilizados no dia a dia da organização;&lt;/li&gt;
&lt;li&gt;Por último, caso seja possível, converse com toda a equipe que será avaliada previamente, relembrando as responsabilidades de cada um.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Depois da auditoria interna finalizada é hora de verificar as não conformidades e oportunidades de melhoria apresentadas. &lt;/p&gt;

&lt;p&gt;Baseando-se em seus procedimentos, verifique o que deve ser feito e tome as devidas providências para resolução dos problemas antes da realização da auditoria externa (certificadora ou de manutenção). &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Passo número 3:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Com auditoria interna e ajustes realizados, chegou a hora da auditoria certificadora. Neste momento, diferentemente da primeira, o objetivo é ter o mínimo de não conformidades e oportunidades de melhoria possíveis. &lt;/p&gt;

&lt;p&gt;A duração é normalmente de 1 semana, tempo em que serão percorridos todos os anexos e seções obrigatórios da normativa. Este processo deve ser realizado por uma empresa credenciada e autorizada, pois a mesma será responsável pela emissão de seu certificado.&lt;/p&gt;

&lt;p&gt;Ao longo da auditoria será necessário o levantamento de evidências para atestar a conformidade de seus processos. Em caso de não conformidades ou oportunidades de melhoria, o auditor deverá informar um prazo limite para resolução dos pontos levantados. &lt;/p&gt;

&lt;p&gt;Ao final, a empresa responsável poderá recomendar sua organização para a certificação, ou, poderá exigir a correção das não conformidades e oportunidades de melhoria para obtenção da certificação.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusão&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mesmo se tratando de um processo trabalhoso, são inúmeras as possibilidades e facilidades a partir da obtenção da Certificação ISO 27001. A acreditação traz benefícios tanto diretos quanto indiretos para dia a dia da sua empresa, sendo alguns deles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maior previsibilidade de eventos e incidentes de segurança da informação,  consequentemente leva à menores custos em seus tratamentos;&lt;/li&gt;
&lt;li&gt;Credibilidade da empresa junto ao mercado consumidor, demonstrando clara e manifesta preocupação com dados e informações;&lt;/li&gt;
&lt;li&gt;Melhoria contínua de processos, documentações e rotinas de trabalho através das constantes auditorias internas e externas&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>iso27001</category>
      <category>security</category>
      <category>startup</category>
    </item>
    <item>
      <title>Nossa Stack</title>
      <dc:creator>Victor Campos</dc:creator>
      <pubDate>Thu, 03 Feb 2022 14:28:06 +0000</pubDate>
      <link>https://dev.to/v360/nossa-stack-55ep</link>
      <guid>https://dev.to/v360/nossa-stack-55ep</guid>
      <description>&lt;p&gt;Aqui na V360 gostamos de deixar nossa stack de tecnologia o mais simples possível.&lt;/p&gt;

&lt;p&gt;Quanto menos nos preocuparmos com ela, mais tempo teremos para pensar nos nossos clientes.&lt;/p&gt;

&lt;p&gt;Gostamos de brincar que qualquer remoção está pré-aprovada. Já para adicionar complexidade, tem que ter uma boa justificativa.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cloud&lt;/strong&gt;&lt;br&gt;
Atualmente utilizamos a AWS para todos os serviços de infraestrutura.&lt;/p&gt;

&lt;p&gt;Usar a AWS nos ajudou tanto em escala quanto em segurança. Saber que estamos em uma nuvem, com os certificados de segurança em dia, traz tranquilidade para nós e confiança para nossos clientes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Banco de Dados Relacional&lt;/strong&gt;&lt;br&gt;
Usamos o Postgres como banco de dados relacional, através do serviço de RDS da AWS.&lt;/p&gt;

&lt;p&gt;Esse sistema se mostrou estável, confiável e performático para nossa aplicação.&lt;/p&gt;

&lt;p&gt;O que nos fez escolhê-lo, ao invés do MySQL, foi a qualidade das transações de esquema, além de algumas funcionalidades, como colunas de jsonb.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Banco de Dados Textual&lt;/strong&gt;&lt;br&gt;
Como trabalhamos com muito dado em formato de texto, adotamos uma prática de indexar os dados no ElasticSearch (também através de serviço da AWS).&lt;/p&gt;

&lt;p&gt;Do ElasticSearch, saem hoje nossos principais relatórios e dashboards, tirando uma boa carga do Postgres.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Banco de Dados de Chave-Valor&lt;/strong&gt;&lt;br&gt;
Usamos atualmente o Redis, tanto para gerenciar jobs assíncronos (com a gema Sidekiq) quanto para o cache da aplicação.&lt;/p&gt;

&lt;p&gt;Deixamos também a AWS gerenciar o Redis através do ElasticCache.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Framework&lt;/strong&gt;&lt;br&gt;
O framework que utilizamos para desenvolvimento da nossa aplicação web é o Ruby on Rails.&lt;/p&gt;

&lt;p&gt;Os principais motivos dessa escolha estão descritos no post &lt;a href="https://dev.to/v360/porque-rails-2cap"&gt;“Por que Rails?”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;De bônus, usamos o Rails Engines para separar nossas aplicações em módulos, tendo um monolito modular.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend&lt;/strong&gt;&lt;br&gt;
Nosso frontend é todo construído com JavaScript vanilla.&lt;/p&gt;

&lt;p&gt;Acreditamos que um frontend simples para o usuário não necessita de ferramentas complexas.&lt;/p&gt;

&lt;p&gt;——&lt;/p&gt;

&lt;p&gt;Com essa stack simples, conseguimos entregar um software de qualidade e escalável, focando no que realmente importa: o cliente final.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>aws</category>
      <category>postgres</category>
    </item>
    <item>
      <title>GitHub como Ferramenta Organizacional</title>
      <dc:creator>Victor Campos</dc:creator>
      <pubDate>Thu, 20 Jan 2022 17:31:04 +0000</pubDate>
      <link>https://dev.to/v360/github-como-ferramenta-organizacional-18b</link>
      <guid>https://dev.to/v360/github-como-ferramenta-organizacional-18b</guid>
      <description>&lt;p&gt;O nosso código dorme hoje dentro do Github e nos últimos seis meses passamos a nos organizar em volta dele.&lt;/p&gt;

&lt;p&gt;Faz todo sentido para a gente, quanto mais próximo a empresa está do nosso código, mais fácil fica a comunicação, identificação de problemas e oportunidades.&lt;/p&gt;

&lt;p&gt;——&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repositório de código&lt;/strong&gt;&lt;br&gt;
Começamos a usar o Github como todos começam, usando para guardar o nosso código.&lt;/p&gt;

&lt;p&gt;Além dele, fizemos fork de algumas gemas que tivemos que fazer alguma modificação para atender nosso interesse.&lt;/p&gt;

&lt;p&gt;Essas gemas ficam em repositório aberto, enquanto nosso código permanece fechado.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issues&lt;/strong&gt;&lt;br&gt;
Todo atendimento de Dev ocorre por meio de issues, as mesmas são divididas em melhorias, bugs ou no code.&lt;/p&gt;

&lt;p&gt;Uma ferramenta que o Github tem, na qual nos mudou de patamar, foi a possibilidade de templates de issues. Com isso, quando qualquer pessoa da empresa quer abrir uma issue, ela já sabe os pontos importantes que precisa preencher.&lt;/p&gt;

&lt;p&gt;O legal é que depois de um tempo toda a empresa mudou para issues. E quando alguma pessoa nova entra por exemplo, é criada uma issue de onboard.&lt;/p&gt;

&lt;p&gt;Assim todo mundo tem visibilidade do que cada time tem de tarefa, aumentando a viabilidade.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Projects&lt;/strong&gt;&lt;br&gt;
Abandonamos o Trello e migramos todos os nosso boards para o Projects.&lt;/p&gt;

&lt;p&gt;As issues deram visibilidade de quais tarefas existiam para cada time. No projects temos a ideia de qual etapa cada issue está no nosso processo de priorização.&lt;/p&gt;

&lt;p&gt;Assim, evitamos a necessidade de reuniões para ficar perguntando onde cada coisa está, e podemos usar as reuniões para focar em como podemos ajudar uns aos outros a mover a issue para frente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Actions&lt;/strong&gt;&lt;br&gt;
Também movemos todo nosso pipe de CI para dentro do Github.&lt;/p&gt;

&lt;p&gt;Assim, o mesmo ficou integrado dentro do nosso processo, sendo executado tanto quando adicionamos uma label específica, quanto por commits.&lt;/p&gt;

&lt;p&gt;E também, geramos os nossos relatórios de releases semanais para informar a empresa tudo que entrou no produto na semana.&lt;/p&gt;

&lt;p&gt;Qualquer pessoa do time tem acesso, por exemplo, a deploys que funcionaram ou quebraram, testes que não passaram, etc…&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pull Request&lt;/strong&gt;&lt;br&gt;
O coração aonde o Dev faz suas tarefas. Todo pull request tem que estar vinculado a uma ou mais issues ao qual vai fechar/avançar.&lt;/p&gt;

&lt;p&gt;Ao ser marcado como pronto para revisão, o próprio Github atribui ele para algum outro Dev revisar, não necessariamente da mesma squad.&lt;/p&gt;

&lt;p&gt;Assim, além de melhorar a qualidade do que está sendo entregue, pois a modificação tem que ser simples para qualquer um entender, garantimos que todo o time tenha algum conhecimento de todas as partes do nosso produto.&lt;/p&gt;

&lt;p&gt;Muitas vezes as melhores decisões de código vêm de pessoas que estão mais afastadas do problema, pois essas tendem a questionar mais as decisões tomadas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Github CLI&lt;/strong&gt;&lt;br&gt;
Infelizmente o insight do Github é bem ruim. Mas resolvemos isso adicionando labels necessárias nas issues e um pouco de código para pegar essas informações pela api do Github.&lt;/p&gt;

&lt;p&gt;Dessa maneira, conseguimos tirar métricas de velocidade do time, quantidade de bugs, etc.&lt;/p&gt;

&lt;p&gt;E assim, podemos saber se as mudanças organizacionais que tomamos estão tendo efeitos positivos ou negativos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discussion&lt;/strong&gt;&lt;br&gt;
Essa ferramenta acabou de deixar fase beta do Github.&lt;/p&gt;

&lt;p&gt;Ela é um fórum interno do mesmo, e usamos para registrar todas as discussões que temos internamente.&lt;/p&gt;

&lt;p&gt;Quando algo começa a se prolongar no Discord, migramos para lá.&lt;/p&gt;

&lt;p&gt;Até para ser fácil pessoas novas entenderem o contexto do porquê as coisas hoje são como são.&lt;/p&gt;

&lt;p&gt;Também registramos resumo de livros que gostamos, e como resolvemos algum problema mais sério dos nossos clientes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wiki&lt;/strong&gt;&lt;br&gt;
Aqui mora toda a nossa documentação, desde documentação de como usar o produto, responder a certos questionamentos do cliente, até nossa boas práticas de programação.&lt;/p&gt;

&lt;p&gt;Também usamos nossa wiki para o nosso processo de onboard. Temos documentado nela (e gosto de acreditar que cada pessoa que entra melhora esse processo) o que se espera que a pessoa saiba a cada semana até conseguir chegar em um ponto que consegue realizar as primeiras atividades sozinha.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Futuro&lt;/strong&gt;&lt;br&gt;
Quem sabe não adotamos no futuro o editor de código que o Github acabou de lançar?&lt;/p&gt;

&lt;p&gt;O que eu posso falar é, cada dia que passa sinto menos necessidade de ter outras ferramentas de gestão de time, produto e código, além do próprio Github.&lt;/p&gt;

</description>
      <category>github</category>
      <category>startup</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Resumo: Remote, Office Not Required</title>
      <dc:creator>Victor Campos</dc:creator>
      <pubDate>Wed, 29 Dec 2021 22:35:32 +0000</pubDate>
      <link>https://dev.to/v360/resumo-remote-office-not-required-4521</link>
      <guid>https://dev.to/v360/resumo-remote-office-not-required-4521</guid>
      <description>&lt;h1&gt;
  
  
  Introdução
&lt;/h1&gt;

&lt;p&gt;Na V360, incentivamos nosso time a se desenvolver no campo pessoal e profissional através da leitura. Como trabalhamos a distância, na ferramenta que nos comunicamos no dia a dia, separamos um canal para compartilharmos e batermos um papo sobre: livros.&lt;/p&gt;

&lt;p&gt;Remote: Office is not Required foi um dos livros em destaque.  E o tema principal dele se conecta com a empresa, pois tivemos que nos reinventar no início da pandemia em 2020, adotando o trabalho remoto. &lt;/p&gt;

&lt;p&gt;E aqui, enxergamos que esse modelo veio para ficar. Nosso time é composto por mais de 60 pessoas espalhadas pelo Brasil, acreditamos que nós rompemos muitas barreiras e nos desenvolvemos cada vez mais, trabalhando de onde quisermos.&lt;/p&gt;

&lt;p&gt;E por isso, resolvi trazer aqui uma resenha sobre o Remote: Office is not Required, selecionando alguns capítulos que chamaram minha atenção nesta leitura.&lt;/p&gt;

&lt;h1&gt;
  
  
  Resumo
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Positivos:
&lt;/h2&gt;

&lt;p&gt;Trabalho remoto não foi adotado antes por falta de tecnologia, mas com o avanço da internet não existe mais desculpas.&lt;/p&gt;

&lt;p&gt;Além do mal para saúde e meio ambiente, um colaborador perde em média 400 horas se locomovendo para/do trabalho.&lt;/p&gt;

&lt;p&gt;A hora que os colaboradores mais produzem são as horas sem interrupção. Ter uma cultura de trabalho assíncrona e com horário realmente flexível é importante para produtividade.&lt;/p&gt;

&lt;p&gt;Trabalho remoto permite às pessoas trabalharem do lugar que mais se adeque ao seu estilo de vida.&lt;/p&gt;

&lt;p&gt;É possível achar talento em qualquer lugar.&lt;/p&gt;

&lt;p&gt;Trabalho remoto não é sobre reduzir custos. É sobre ter colaboradores com qualidade de vida, onde desperdiçam menos tempo com o que não importa. Além disso, o trabalho remoto também economiza dinheiro tanto da empresa com redução de custo do escritório, quanto dos colaboradores com transporte e alimentação.&lt;/p&gt;

&lt;p&gt;Não é tudo ou nada. As empresas podem continuar tendo escritório, eles só não podem ser obrigatórios. Alguns colaboradores/funções vão continuar preferindo se reunir no escritório por motivos diversos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Negativos:
&lt;/h2&gt;

&lt;p&gt;As vezes nos sentimos perdidos sem poder estar na mesma sala dos nossos colegas para “trocar uma ideia”. Usar a tecnologia a favor, como vídeo conferência e transmissão de tela, ajuda nesse ponto.&lt;/p&gt;

&lt;p&gt;Trabalhar fora de sincronização com seus colegas, apesar de ajudar na diminuição da interrupção, gera uma necessidade de ser mais organizado para evitar procrastinação. “Sometimes, distractions can actually serve a purpose. Like the proverbial canary in the coal mine, they warn us—when we feel ourselves regularly succumbing to them—that our work is not well defined, or our tasks are menial, or the whole project we’re engaged in is fundamentally pointless.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Dificuldades a serem superadas
&lt;/h2&gt;

&lt;p&gt;A segurança no escritório é mais controlada (até certo ponto). É completamente possível fazer políticas de segurança que se adequem ao trabalho remoto, algumas dicas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Criptografar o disco&lt;/li&gt;
&lt;li&gt;Travar a tela depois de 10 minutos de inativação &lt;/li&gt;
&lt;li&gt;Sempre usar HTTPS &lt;/li&gt;
&lt;li&gt;Colocar senhas nos celulares &lt;/li&gt;
&lt;li&gt;Gerar uma senha diferente para cada site&lt;/li&gt;
&lt;li&gt;Ligar autenticação de dois fatores sempre que possível &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notas do Victor: “As dicas são bem básicas e estão contidas na nossa política de segurança, mas tem outras coisas que também podemos fazer para nos deixar mais seguros. Recomendo todos relerem a nossa política de tempo em tempo e acompanhar a newsletter de segurança”.&lt;/p&gt;

&lt;p&gt;Estar disponível para o cliente com sistema de horário flexível é um desafio, mas que se resolve com o próprio time se organizando para isso. Talvez, para colaboradores que tenham a função primária de atender o cliente, esses terão que ter um horário mais regular.&lt;/p&gt;

&lt;p&gt;Alguns trabalhos não podem ser feitos remotos. Nesse caso, esses colaboradores não terão esse benefício de trabalhar remoto. Não é porque você não pode (ainda?) fazer a vida de todos melhores, que vai forçar a vida de todos serem piores. Trabalhos diferentes tem necessidades diferentes. (Assim como existem salários diferentes, bônus diferentes, etc…).&lt;/p&gt;

&lt;p&gt;Ter as respostas de maneira mais imediata é difícil trabalhando remoto. Bom, primeiro temos que reconhecer que nem todas as perguntas são igualmente urgentes. Criar canais diferentes para cada tipo de mensagem é importante, ter um canal para mensagens que podem esperar horas para serem respondidas (deveria ser 80% das mensagens), ter um canal que pode esperar minutos para as mensagens (19% das mensagens) e ter um canal de telefone vermelho (1% das mensagens).&lt;/p&gt;

&lt;h2&gt;
  
  
  Como colaborar remotamente
&lt;/h2&gt;

&lt;p&gt;Ter no mínimo 4 horas de overlap com o time on-line ao mesmo tempo. Parece ser o tempo ideal para facilitar comunicação, criar um sentimento de time e ainda assim ter um tempo livre sem distrações.&lt;/p&gt;

&lt;p&gt;Use ferramentas de compartilhamento de tela para melhorar a comunicação. Ver ainda é importante.&lt;/p&gt;

&lt;p&gt;Use ferramentas de gravar a tela para mostrar as funcionalidades que está trabalhando ou os problemas que você encontrou. Fica mais fácil entender a feature/bug vendo do que lendo uma descrição.&lt;/p&gt;

&lt;p&gt;A agenda do time, os documentos necessários e as discussões, devem estar sempre disponíveis para os outros colaboradores do time.&lt;/p&gt;

&lt;p&gt;Tenha seu bebedouro de água virtual, onde as pessoas podem ir para espairecer. Pode parecer um desperdício de tempo, mas na verdade é um tempo bem gasto com os colegas de trabalho.&lt;/p&gt;

&lt;p&gt;Mantenha todos no loop. É necessário manter as pessoas bem informadas para criar um fluxo de informações dentro da empresa, gerando a sensação de que estamos todos no mesmo barco.&lt;/p&gt;

&lt;p&gt;Reuniões não são intrinsicamente ruins, elas são como o sal, algumas pitadas deixam o prato melhor, mas estraga a comida e a saúde se usada de mais. A maioria dos problemas deveria conseguir ser resolvido sem uma reunião. E lembre-se, não existe isso de reunião de 1 hora. Se você está em uma reunião de 1 hora com 5 pessoas, são 5 horas investidas naquela reunião. O problema tem que valer esse investimento.&lt;/p&gt;

&lt;p&gt;Sobre gerenciamento, ele continua sendo importante. Porém, para funcionar bem no trabalho remoto é necessário que o time mantenha os status atualizados e o gerente acredite neles, para não precisar interromper as pessoas perguntando como as coisas estão indo.&lt;/p&gt;

&lt;p&gt;O isolamento é um problema no trabalho remoto.  Por mais que a comunicação flua bem através dos canais digitais, as pessoas ainda precisam de pessoas reais. Um aprendizado do livro é que essas interações com pessoas reais não precisam vir de colegas de trabalho, pode ser seu cônjuge, filho(a), vizinhos e amigos, passe o tempo extra ganho no trânsito com eles.&lt;/p&gt;

&lt;p&gt;Se você contratou pessoas que gostam do trabalho que fazem, é mais provável que elas trabalhem mais remoto do que menos, esticando o dia de trabalho para 10~12 horas. É necessário dobrar a atenção com burnouts, e a melhor forma de fazer isso é criar a cultura de expectativas razoáveis.&lt;/p&gt;

&lt;p&gt;Incentive os colaboradores a se movimentarem e serem saudáveis. A saúde deles é muito importante para a empresa.&lt;/p&gt;

&lt;p&gt;Com os clientes, é importante gerar confiança desde o início e deixar claro que o trabalho é remoto. Além disso, é preciso redobrar a atenção nas respostas com eles, responder e-mails, mensagens e retornar telefonemas se tornam primordiais. A distância causa ansiedade e temos que manter o contato para diminuir a mesma. E por último, mostre o progresso, quanto mais progresso a ser mostrado, melhor.&lt;/p&gt;

&lt;p&gt;Trabalhar remotamente permite às pessoas se mudarem e continuarem na empresa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contratação:
&lt;/h2&gt;

&lt;p&gt;Habilidades sociais são mais importantes remotamente do que socialmente. Principalmente a habilidade de escrever.&lt;/p&gt;

&lt;p&gt;Desafios com casos reais são melhores do que desafios inventados para medir o fit com a empresa. Se está contratando um desenvolvedor, olhe código. Se está contratando alguém de Customer Success, olhe como essa pessoa responde dúvidas reais de clientes.&lt;/p&gt;

&lt;p&gt;Pague salários iguais independente da onde a pessoa for. Pode parecer tentador pagar salários menores para quem vive um custo de vida menor, mas se você tem condições de pagar salários melhores do que os salários locais de onde a pessoa mora, use isso para contratar os melhores daquele lugar, não brigar pelos medianos (aqui no sentido de estar na mediana mesmo).&lt;/p&gt;

&lt;p&gt;Procure duas qualidades em novos colaboradores: Inteligência e “Gets things Done”.&lt;/p&gt;

&lt;p&gt;Como já dito, a habilidade de escrever bem é primordial. Como a maioria dos argumentos e comunicação vão ser de forma escrita, escrever bem é um must have.&lt;/p&gt;

&lt;p&gt;Tenha um teste prático no processo seletivo, nunca avalie um candidato só pelo currículo.&lt;/p&gt;

&lt;p&gt;No fim, avalie a cultura.  Você espera o resultado, mas convive com a pessoa, mesmo remotamente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gerenciando trabalho remoto 
&lt;/h2&gt;

&lt;p&gt;Não gerencie horário de trabalho, gerencie as entregas. Colocar as pessoas certas para fazer as coisas certas, dividir corretamente o trabalho, ajudar a superar problemas, etc. Esse deveria ser a forma correta de gerenciar o trabalho.&lt;/p&gt;

&lt;p&gt;Se reúna presencialmente 2 vezes por ano pelo menos.&lt;/p&gt;

&lt;p&gt;Aprenda com o gerenciamento de open source: projetos open source batem de frente com grandes empresas, com colaboração remota e assíncrona. Aprender com eles é fundamental. Algumas das suas características são: “Motivação intrínseca”, “Informações abertas”, “Encontros casuais ao vivo”.&lt;/p&gt;

&lt;p&gt;Mantenha um ambiente em que não diferencie profissionais que trabalham remoto dos que trabalham presencial.&lt;/p&gt;

&lt;p&gt;Faça reuniões de 1:1 frequentemente, uma vez a cada poucos meses pelo menos.&lt;/p&gt;

&lt;p&gt;Remova empecilhos, trabalhar assíncrono significa que nem sempre você vai poder contar com a pessoa na hora que você precisa. Por isso, quanto menos você precisar melhor.&lt;/p&gt;

&lt;p&gt;Empodere as pessoas e crie um ambiente de confiança para que elas consigam tomar as decisões sem terem medo.&lt;/p&gt;

&lt;p&gt;Ter cuidado com a sobrecarga mais do que com a carga de menos trabalho. Se você contratou correto é um problema muito mais provável de ocorrer. Lembre-se que você está em uma maratona, não em uma sprint.&lt;/p&gt;

&lt;p&gt;Use a escassez a seu favor. Como conversas cara a cara (video chat) vão ser menos frequentes, elas vão ter mais valor. Use ela para passar os recados mais importantes.&lt;/p&gt;

&lt;h2&gt;
  
  
  O dia a dia de um trabalhador remoto
&lt;/h2&gt;

&lt;p&gt;Crie uma rotina, é muito fácil se perder nas horas trabalhando remoto e não conseguir separar trabalho do dia a dia. Ter as suas roupas de trabalho e roupas de não trabalho, pois mudar de roupa ajuda você também a delimitar quando está trabalhando. E se possível, tenha um local para trabalho separado e/ou delimite locais onde o trabalho é proibido (dica, seu quarto).&lt;/p&gt;

&lt;p&gt;Trabalho remoto não precisa ser tudo ou nada, você pode passar alguns dias no escritório e alguns dias em casa, ou mesmo parte do dia em casa e parte do dia no escritório. Trabalho remoto é sobre flexibilidade.&lt;/p&gt;

&lt;p&gt;Tenha um computador separado para trabalhar do seu computador para diversão.&lt;/p&gt;

&lt;p&gt;Se ajudar, busque um local de trabalho perto da sua casa, um coffee shop ou um coworking.&lt;/p&gt;

&lt;p&gt;Seja sincero sobre o que está tirando sua motivação. Motivação é algo que vem de dentro, dificilmente as outras pessoas da equipe vão saber os motivos da sua falta de motivação do que você mesmo. Se expressar é importante para resolver o problema.&lt;/p&gt;

&lt;p&gt;Se permita trabalhar em viagens (não durante as férias), mas use a liberdade do trabalho remoto para conhecer lugares novos.&lt;/p&gt;

&lt;p&gt;Passe tempo com sua família.&lt;/p&gt;

</description>
      <category>resumo</category>
      <category>livros</category>
      <category>startup</category>
    </item>
    <item>
      <title>Por que Rails?</title>
      <dc:creator>Victor Campos</dc:creator>
      <pubDate>Thu, 16 Dec 2021 13:50:56 +0000</pubDate>
      <link>https://dev.to/v360/porque-rails-2cap</link>
      <guid>https://dev.to/v360/porque-rails-2cap</guid>
      <description>&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;As primeiras linhas de código do V360 foram escritas em 2016. Mas se fôssemos reescrevê-lo hoje, em 2021, ainda usaríamos Ruby On Rails?&lt;/p&gt;

&lt;p&gt;A resposta é &lt;strong&gt;&lt;em&gt;sim&lt;/em&gt;&lt;/strong&gt;!&lt;br&gt;
E o objetivo desse post é dizer o porquê.&lt;/p&gt;

&lt;p&gt;———&lt;/p&gt;
&lt;h2&gt;
  
  
  Comunidade
&lt;/h2&gt;

&lt;p&gt;O Ruby On Rails nasceu em 2003 e, 18 anos depois, a sua comunidade continua forte. Empresas maduras como Github, Gitlab, Shopify, Basecamp e Stripe (&lt;a href="https://www.statista.com/chart/19317/highest-valued-startup-companies-in-the-world/" rel="noopener noreferrer"&gt;segunda startup mais valiosa do mundo&lt;/a&gt;, continuam utilizando-o.&lt;/p&gt;

&lt;p&gt;De um tempo para cá, iniciou-se um movimento dessas empresas de atualização do framework e suas gemas para a última versão, trazendo uma gigantesca força financeira e de pessoas para o desenvolvimento do framework.&lt;/p&gt;

&lt;p&gt;Github: &lt;a href="https://github.blog/2020-08-25-upgrading-github-to-ruby-2-7/" rel="noopener noreferrer"&gt;https://github.blog/2020-08-25-upgrading-github-to-ruby-2-7/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gitlab: &lt;a href="https://about.gitlab.com/blog/2020/07/08/migrating-to-puma-on-gitlab/" rel="noopener noreferrer"&gt;https://about.gitlab.com/blog/2020/07/08/migrating-to-puma-on-gitlab/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shopify: &lt;a href="https://shopify.engineering/upgrading-shopify-to-rails-5-0" rel="noopener noreferrer"&gt;https://shopify.engineering/upgrading-shopify-to-rails-5-0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;E não apenas empresas grandes e consolidadas, como essas já citadas, usam o Rails. Novas startups - como Linkana, Beep Saúde, Donorbox, Dev.to e outras mais - trazem inovação para o ecossistema.&lt;/p&gt;

&lt;p&gt;Essa mistura de estabilidade e inovação faz o ecossistema Rails caminhar numa direção correta.&lt;/p&gt;
&lt;h2&gt;
  
  
  Times Pequenos
&lt;/h2&gt;

&lt;p&gt;Uma startup não tem dinheiro, nem no início, nem nos primeiros anos.&lt;/p&gt;

&lt;p&gt;Além disso, a escassez de talentos no mercado está cada vez mais visível, seja para empresas grandes, seja para startups. Em qualquer linguagem.&lt;/p&gt;

&lt;p&gt;Então, ter uma tecnologia como o Rails, que é otimizada para times pequenos, é fundamental.&lt;/p&gt;

&lt;p&gt;A arquitetura de "&lt;a href="https://blog.appsignal.com/2020/04/08/the-citadel-architecture-at-appsignal.html" rel="noopener noreferrer"&gt;citadel&lt;/a&gt;" (explicada neste ótimo artigo da Appsignal - outra startup em Rails), permite que poucos desenvolvedores façam o trabalho de muitos.&lt;/p&gt;
&lt;h2&gt;
  
  
  Evita decisões desnecessárias
&lt;/h2&gt;

&lt;p&gt;Muitas das nossas decisões técnicas, no fim do dia, não geram valor algum para o cliente.&lt;/p&gt;

&lt;p&gt;O Rails busca tomar exatamente essas decisões por você, fazendo com que consiga focar naquilo que realmente importa para seu usuário final.&lt;/p&gt;

&lt;p&gt;Ao seguir as famosas convenções do Rails, você evita tomar dezenas (ou centenas?) de micro decisões, e as coisas simplesmente funcionam como "mágica".&lt;/p&gt;
&lt;h2&gt;
  
  
  Respira a Web
&lt;/h2&gt;

&lt;p&gt;O Rails nunca tentou brigar com a Web. Muito pelo contrário, ele busca se intrometer somente onde é realmente necessário.&lt;/p&gt;

&lt;p&gt;Um exemplo disso é a decisão de &lt;a href="https://world.hey.com/dhh/modern-web-apps-without-javascript-bundling-or-transpiling-a20f2755" rel="noopener noreferrer"&gt;remover o Webpacker&lt;/a&gt; como padrão no Rails 7. A web já chegou no nível de não precisar mais dele.&lt;/p&gt;

&lt;p&gt;E qual a beleza nisso? A beleza é que a própria web já é uma das maiores criações da humanidade. Diferente de uma aplicação desktop, um site web escrito no dia 1 continua funcionando até hoje.&lt;/p&gt;

&lt;p&gt;Ela tem defeitos, é óbvio. Mas no geral, as soluções para esses defeitos que tentaram sobrescrever a web ficaram para trás, como Flash e Java applets, por exemplo.&lt;/p&gt;

&lt;p&gt;Até mesmo a tentativa do virtual dom, do React/Angular, já está começando a &lt;a href="https://svelte.dev/blog/virtual-dom-is-pure-overhead" rel="noopener noreferrer"&gt;ser questionada&lt;/a&gt;. Basta ver o crescimento do Svelte na comunidade JavaScript.&lt;/p&gt;

&lt;p&gt;O Rails, em 2003, percebeu que a melhor maneira era abraçar a web como ela é, e atuar somente no necessário e enquanto necessário.&lt;/p&gt;
&lt;h2&gt;
  
  
  Rails Escala
&lt;/h2&gt;

&lt;p&gt;Apesar de toda a brincadeira do "Rails não escala", a verdade é que ao longo do tempo ele se mostrou bem escalável.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1404835709278580739-373" src="https://platform.twitter.com/embed/Tweet.html?id=1404835709278580739"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1404835709278580739-373');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1404835709278580739&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-885664106677710850-908" src="https://platform.twitter.com/embed/Tweet.html?id=885664106677710850"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-885664106677710850-908');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=885664106677710850&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Assim, estamos confiantes de que conseguiremos crescer ainda MUITO e por muito tempo usando Ruby On Rails.&lt;/p&gt;

&lt;p&gt;——&lt;/p&gt;

&lt;p&gt;Poderíamos citar vários outros motivos que nos levariam a escolhê-lo novamente, mas aí o post se estenderia demais! :)&lt;/p&gt;

&lt;p&gt;Se você quiser saber um pouco mais sobre o assunto ou ficou com alguma curiosidade, deixe aí nos comentários.&lt;/p&gt;

&lt;p&gt;Obrigado, e esperamos que a leitura tenha valido a pena!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>startup</category>
    </item>
  </channel>
</rss>
