<?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: José Gonçalves</title>
    <description>The latest articles on DEV Community by José Gonçalves (@jos_gonalves_fac39f3437).</description>
    <link>https://dev.to/jos_gonalves_fac39f3437</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3771459%2F56251b96-a2a1-4721-843d-be6b01e88b3c.jpg</url>
      <title>DEV Community: José Gonçalves</title>
      <link>https://dev.to/jos_gonalves_fac39f3437</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jos_gonalves_fac39f3437"/>
    <language>en</language>
    <item>
      <title>The Developer's Guide to Brazilian Fintech: Building Payment Systems With Pix, Boleto, and Cards</title>
      <dc:creator>José Gonçalves</dc:creator>
      <pubDate>Sun, 15 Feb 2026 13:28:25 +0000</pubDate>
      <link>https://dev.to/jos_gonalves_fac39f3437/the-developers-guide-to-brazilian-fintech-building-payment-systems-with-pix-boleto-and-cards-2p04</link>
      <guid>https://dev.to/jos_gonalves_fac39f3437/the-developers-guide-to-brazilian-fintech-building-payment-systems-with-pix-boleto-and-cards-2p04</guid>
      <description>&lt;p&gt;If you're building fintech products for Brazil, you're entering one of the most exciting — and complex — payment ecosystems in the world. Pix processed over R$17 trillion in 2023. Credit card installments are a cultural institution. Boleto bancário refuses to die. And the Central Bank keeps launching new features that reshape the landscape every few months.&lt;/p&gt;

&lt;p&gt;At Mind Group Technologies, we've built payment systems for fintech clients across Brazil since 2016. From processing Pix payments in real-time to handling credit card installments across multiple acquirers, we've learned that Brazilian payments are a world unto themselves.&lt;/p&gt;

&lt;p&gt;Here's the practical guide I wish someone had given me when we started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Brazilian Payment Landscape
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pix: The Game Changer
&lt;/h3&gt;

&lt;p&gt;Pix launched in November 2020 and fundamentally changed how Brazilians move money. Within three years, it became the most-used payment method in the country. The numbers are staggering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;150+ million&lt;/strong&gt; registered Pix keys&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;R$17+ trillion&lt;/strong&gt; processed in 2023&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Average transaction time&lt;/strong&gt;: 3-10 seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Available&lt;/strong&gt;: 24/7/365, including holidays&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost to merchants&lt;/strong&gt;: Free for individuals, near-zero for businesses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For developers, Pix is accessed through the banking APIs of authorized Payment Service Providers (PSPs). The Central Bank defines the standard, but each PSP implements it slightly differently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified Pix payment flow&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createPixPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Generate a unique transaction ID&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;txId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateTxId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Max 35 chars, alphanumeric&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Create the charge via PSP API&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;charge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pspClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCharge&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;calendario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;expiracao&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// 1 hour expiration&lt;/span&gt;
    &lt;span class="na"&gt;devedor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cpf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customerCPF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;nome&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customerName&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;original&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;chave&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;merchantPixKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Merchant's Pix key&lt;/span&gt;
    &lt;span class="na"&gt;solicitacaoPagador&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;infoAdicionais&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;nome&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tenant_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tenantId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;nome&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;orderId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Generate QR Code&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;qrCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pspClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateQRCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;charge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;txId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;qrCodeImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;qrCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;imagemQrcode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Base64 PNG&lt;/span&gt;
    &lt;span class="na"&gt;qrCodePayload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;qrCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;qrcode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// Copy-paste string&lt;/span&gt;
    &lt;span class="na"&gt;expiresAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3600000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Critical implementation detail&lt;/strong&gt;: Pix confirmations come via webhooks. You MUST implement idempotent webhook handlers because the Central Bank may send duplicate notifications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Idempotent Pix webhook handler&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handlePixWebhook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;txid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;horario&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// Check if already processed (idempotency)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT id FROM pix_transactions WHERE txid = $1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;txid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;already_processed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Process within a transaction&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;trx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;INSERT INTO pix_transactions (txid, amount, timestamp) VALUES ($1, $2, $3)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;txid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;valor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;original&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;horario&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;trx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UPDATE orders SET payment_status = $1 WHERE pix_txid = $2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirmed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;txid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Notify customer in real-time&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;notifyPaymentConfirmed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;txid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;processed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Credit Card Installments (Parcelamento)
&lt;/h3&gt;

&lt;p&gt;This is where Brazilian payments get uniquely complex. In most countries, you charge a credit card once. In Brazil, customers expect to split purchases into monthly installments — and they get upset if you don't offer it.&lt;/p&gt;

&lt;p&gt;There are two types:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Parcelamento sem juros&lt;/strong&gt; (interest-free installments): The merchant absorbs the cost. The customer pays R$100 in 3x of R$33.33.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parcelamento com juros&lt;/strong&gt; (installments with interest): The customer pays interest. R$100 becomes 3x of R$35.50.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Calculate installment options&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;calculateInstallments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;totalAmount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxInstallments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;maxInstallments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;installments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;installmentAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;totalAmount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;totalAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;totalAmount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;interestFree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`1x de R$ &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;totalAmount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; (à vista)`&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Interest-free up to 3x (merchant absorbs MDR)&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;installmentAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;totalAmount&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;installments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;installmentAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;installmentAmount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;totalAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;totalAmount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;interestFree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;x de R$ &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;installmentAmount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; sem juros`&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// With interest (typically 1.99% per month)&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;monthlyRate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0199&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;installmentAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;totalAmount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; 
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;monthlyRate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;monthlyRate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&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="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;monthlyRate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalWithInterest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;installmentAmount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;installments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;installmentAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;installmentAmount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;totalAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;totalWithInterest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;interestFree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;x de R$ &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;installmentAmount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; (total: R$ &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;totalWithInterest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Business consideration&lt;/strong&gt;: The number of interest-free installments you offer directly impacts your conversion rate. Offering 3x sem juros is nearly standard in Brazilian e-commerce. Offering 6x or 12x sem juros can increase conversion 20-40% but eats into your margins.&lt;/p&gt;

&lt;h3&gt;
  
  
  Boleto Bancário: The Undead Payment Method
&lt;/h3&gt;

&lt;p&gt;Boleto is a uniquely Brazilian payment slip that can be paid at any bank, lottery house, or through banking apps. It's been "dying" for a decade but still processes billions in transactions because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;~30 million Brazilians don't have credit cards&lt;/li&gt;
&lt;li&gt;Businesses use it for B2B transactions&lt;/li&gt;
&lt;li&gt;Some customers prefer it for large purchases
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Generate a boleto&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateBoleto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;boleto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;boletoProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;cpf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cpf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Não receber após o vencimento&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Multa de 2% após vencimento&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;fine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;percentage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;// Late fee&lt;/span&gt;
    &lt;span class="na"&gt;interest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;percentage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// Monthly interest on late payment&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;barcode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boleto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;barcode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;digitableLine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boleto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;digitableLine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Typeable line&lt;/span&gt;
    &lt;span class="na"&gt;pdfUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boleto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pdfUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dueDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// Important: boletos take 1-3 business days to confirm&lt;/span&gt;
    &lt;span class="na"&gt;expectedConfirmation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;addBusinessDays&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key difference from other methods&lt;/strong&gt;: Boleto confirmation is NOT real-time. It can take 1-3 business days. Your system needs to handle this async flow gracefully — hold the order, send reminders, and handle expiration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a Multi-Method Payment Service
&lt;/h2&gt;

&lt;p&gt;At Mind Group, we built a payment abstraction layer that handles all Brazilian payment methods through a unified interface. Here's the architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────────────────────┐
│         Payment Gateway Service           │
│                                          │
│  ┌──────────────────────────────────┐    │
│  │     Unified Payment Interface     │    │
│  │  createPayment(method, amount)    │    │
│  │  getStatus(paymentId)             │    │
│  │  refund(paymentId, amount)        │    │
│  └──────────────────────────────────┘    │
│           │           │          │        │
│  ┌────────┤   ┌───────┤   ┌─────┤        │
│  ▼        ▼   ▼       ▼   ▼     ▼        │
│ ┌────┐ ┌────┐ ┌──────┐ ┌──────┐         │
│ │Pix │ │Card│ │Boleto│ │Wallet│         │
│ │Adpt│ │Adpt│ │Adapt │ │Adapt │         │
│ └────┘ └────┘ └──────┘ └──────┘         │
│   │      │       │        │              │
├───┼──────┼───────┼────────┼──────────────┤
│   ▼      ▼       ▼        ▼              │
│  PSP   Acquirer  Bank   Digital          │
│  API    API      API    Wallet           │
└──────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each adapter implements the same interface but handles the specifics of each payment method. This means our application code doesn't need to know whether a customer is paying with Pix or credit card — the payment service handles the differences.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling Multiple Acquirers
&lt;/h3&gt;

&lt;p&gt;In Brazil, it's common to work with multiple credit card acquirers (Cielo, Rede, Stone, PagSeguro) for redundancy and better rates. We implemented an intelligent routing system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Acquirer routing logic&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;routeCardPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cardBrand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;installments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tenantId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;payment&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Get tenant's acquirer configuration&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getTenantPaymentConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Score each available acquirer&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;acquirers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acquirer&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;acquirer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;calculateScore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acquirer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Lower MDR = better score&lt;/span&gt;
      &lt;span class="na"&gt;mdr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;acquirer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cardBrand&lt;/span&gt;&lt;span class="p"&gt;]?.[&lt;/span&gt;&lt;span class="nx"&gt;installments&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// Higher uptime = better score&lt;/span&gt;
      &lt;span class="na"&gt;uptime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;acquirer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uptime30d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// Lower latency = better score  &lt;/span&gt;
      &lt;span class="na"&gt;latency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;acquirer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;avgLatencyMs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// Current success rate matters&lt;/span&gt;
      &lt;span class="na"&gt;successRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;acquirer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;successRate24h&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;

  &lt;span class="c1"&gt;// Sort by score, try best first with fallback&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sorted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;acquirer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;acquirer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;processPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payment&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Acquirer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;acquirer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; failed, trying next`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PaymentError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;All acquirers failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Compliance: What You Can't Ignore
&lt;/h2&gt;

&lt;h3&gt;
  
  
  LGPD (Brazil's GDPR)
&lt;/h3&gt;

&lt;p&gt;Financial data is the most sensitive category under LGPD. Requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Explicit consent&lt;/strong&gt; for data collection and processing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data minimization&lt;/strong&gt; — only collect what you need&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Right to deletion&lt;/strong&gt; — customers can request data removal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breach notification&lt;/strong&gt; — 72 hours to notify the authority (ANPD)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DPO requirement&lt;/strong&gt; — you need a Data Protection Officer&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Central Bank Regulations
&lt;/h3&gt;

&lt;p&gt;If you're processing payments, you need to comply with Central Bank of Brazil (BCB) regulations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resolution 4,658&lt;/strong&gt;: Cloud computing requirements for financial institutions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Circular 3,978&lt;/strong&gt;: Anti-money laundering (PLD/FT) requirements&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PIX regulations&lt;/strong&gt;: Specific rules for Pix participants&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  PCI DSS
&lt;/h3&gt;

&lt;p&gt;If you handle credit card data, PCI compliance is mandatory. Our recommendation: use tokenization through your acquirer's vault. Never store raw card numbers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// NEVER do this&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;cardNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;4111111111111111&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// RAW CARD NUMBER - VIOLATION&lt;/span&gt;
  &lt;span class="na"&gt;cvv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// DO this instead - tokenize through the acquirer&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;acquirer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tokenize&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;encryptedCard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;rsaEncrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cardData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;acquirerPublicKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Store and reuse the token&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;cardToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Safe to store&lt;/span&gt;
  &lt;span class="na"&gt;acquirerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cielo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Pix Innovations to Watch
&lt;/h2&gt;

&lt;p&gt;The Central Bank keeps evolving Pix with new features that create opportunities for developers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pix Automático&lt;/strong&gt; (automatic recurring Pix) — subscription billing without credit cards&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pix por Aproximação&lt;/strong&gt; (contactless Pix) — NFC payments at physical stores&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pix Garantido&lt;/strong&gt; (guaranteed Pix) — installment payments via Pix, potentially replacing credit card parcelamento&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pix Internacional&lt;/strong&gt; — cross-border Pix payments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these creates new integration requirements and business opportunities. At Mind Group, we're already building support for Pix Automático into our multi-tenant payment platform, as it could significantly reduce payment processing costs for our fintech clients.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Tips From the Trenches
&lt;/h2&gt;

&lt;p&gt;After 8+ years building payment systems in Brazil:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Always have a fallback acquirer.&lt;/strong&gt; Single points of failure in payments cost real money.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test with real money in sandbox.&lt;/strong&gt; Brazilian payment sandbox environments are notoriously unreliable. Test with small real transactions before going live.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Handle CPF validation properly.&lt;/strong&gt; CPF (Brazilian tax ID) is required for most payment methods. Validate the check digits, don't just check length.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Time zone matters.&lt;/strong&gt; Brazil has multiple time zones and DST rules change. Payment deadlines (especially boleto) must account for this.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reconciliation is harder than processing.&lt;/strong&gt; Build automated reconciliation from day one. When you process thousands of transactions across multiple methods and acquirers, manual reconciliation is impossible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitor chargebacks aggressively.&lt;/strong&gt; Brazil has one of the highest chargeback rates in the world. Implement fraud detection (device fingerprinting, velocity checks, address verification) before you need it.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Opportunity
&lt;/h2&gt;

&lt;p&gt;Brazil's fintech sector attracted over $3 billion in investment in recent years. The Central Bank is one of the most innovation-friendly regulators in the world. And there are still massive gaps — 30+ million unbanked adults, SMBs struggling with payment infrastructure, and entire sectors that haven't digitized their payment flows.&lt;/p&gt;

&lt;p&gt;For developers willing to navigate the complexity, Brazilian fintech is one of the most rewarding spaces to build in. The payment ecosystem is rich, the market is enormous, and the problems are real.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;José Gonçalves is the Founder of Mind Group Technologies, a software company in Sorocaba, Brazil, specializing in fintech, healthcare, and SaaS platforms. We've been building payment systems and multi-tenant platforms since 2016. Learn more at &lt;a href="https://mindconsulting.com.br" rel="noopener noreferrer"&gt;mindconsulting.com.br&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building Scalable Delivery Platforms in Brazil: What We Learned Powering 30+ Brands</title>
      <dc:creator>José Gonçalves</dc:creator>
      <pubDate>Sun, 15 Feb 2026 13:21:47 +0000</pubDate>
      <link>https://dev.to/jos_gonalves_fac39f3437/building-scalable-delivery-platforms-in-brazil-what-we-learned-powering-30-brands-1na1</link>
      <guid>https://dev.to/jos_gonalves_fac39f3437/building-scalable-delivery-platforms-in-brazil-what-we-learned-powering-30-brands-1na1</guid>
      <description>&lt;p&gt;Brazil's delivery market is one of the most competitive in the world. iFood dominates with 80%+ market share, Rappi fights for second place, and hundreds of regional players compete for what's left. But here's something most people don't realize: behind many of those regional delivery brands, there's often a single white-label platform powering everything.&lt;/p&gt;

&lt;p&gt;At Mind Group Technologies, we built that platform. Since 2018, our multi-tenant delivery infrastructure has powered 30+ brands across Brazil — from food delivery to pharmacy logistics to grocery services. Each brand has its own identity, its own customers, and its own business rules. But under the hood, they share a battle-tested codebase.&lt;/p&gt;

&lt;p&gt;Here's what we learned building it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why White-Label Delivery Works in Brazil
&lt;/h2&gt;

&lt;p&gt;Brazil has 5,570 municipalities. iFood operates in roughly 1,500 of them. That leaves over 4,000 cities where local entrepreneurs see an opportunity to build delivery services tailored to their communities.&lt;/p&gt;

&lt;p&gt;These entrepreneurs don't have $5 million to build a delivery platform from scratch. They need something that works out of the box but can be customized — their brand, their rules, their pricing model.&lt;/p&gt;

&lt;p&gt;That's the white-label proposition: instead of building from zero, you get a proven platform with your logo on it. Your customers never know (or care) that the same technology powers the delivery app in the next city over.&lt;/p&gt;

&lt;p&gt;The economics are compelling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Custom build&lt;/strong&gt;: $200K-500K upfront + $15K-30K/month maintenance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;White-label platform&lt;/strong&gt;: $2K-8K/month, operational in 2-4 weeks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time to market&lt;/strong&gt;: 12-18 months vs 2-4 weeks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Technical Architecture That Makes It Work
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Multi-Tenant Core
&lt;/h3&gt;

&lt;p&gt;Every delivery platform needs to handle orders, payments, logistics, and communication. In a multi-tenant system, these are shared services with tenant-specific configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────┐
│           API Gateway (Kong)             │
│    Tenant resolution via subdomain/JWT   │
├─────────────────────────────────────────┤
│                                         │
│  ┌──────────┐ ┌──────────┐ ┌─────────┐ │
│  │  Order    │ │ Payment  │ │Logistics│ │
│  │ Service   │ │ Service  │ │ Service │ │
│  │ (Node.js) │ │  (Go)    │ │  (Go)   │ │
│  └──────────┘ └──────────┘ └─────────┘ │
│                                         │
│  ┌──────────┐ ┌──────────┐ ┌─────────┐ │
│  │  User    │ │  Store   │ │  Notif  │ │
│  │ Service  │ │ Service  │ │ Service │ │
│  │(Node.js) │ │(Node.js) │ │(Node.js)│ │
│  └──────────┘ └──────────┘ └─────────┘ │
│                                         │
├─────────────────────────────────────────┤
│    PostgreSQL (RLS) │ Redis │ RabbitMQ  │
└─────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API Gateway handles tenant resolution. Every request includes a tenant identifier — either through the subdomain (brand-a.delivery.com vs brand-b.delivery.com) or embedded in the JWT token. Downstream services never operate without tenant context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Database Strategy: PostgreSQL + Row-Level Security
&lt;/h3&gt;

&lt;p&gt;We use a shared PostgreSQL database with Row-Level Security (RLS). Every table has a &lt;code&gt;tenant_id&lt;/code&gt; column, and RLS policies ensure that queries are automatically filtered by tenant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Create policy for orders table&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;POLICY&lt;/span&gt; &lt;span class="n"&gt;tenant_isolation&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
    &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.current_tenant'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Set tenant context at connection level&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_tenant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'tenant-uuid-here'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- This query automatically returns only current tenant's orders&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'pending'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why RLS over application-level filtering? Because application-level filtering relies on developers never forgetting a WHERE clause. In a codebase with 50+ developers touching it over the years, someone WILL forget. RLS makes isolation a database-level guarantee, not an application-level hope.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-Time Order Tracking
&lt;/h3&gt;

&lt;p&gt;Delivery platforms live or die by real-time tracking. Customers want to see their order moving on a map. Restaurants want to know when a driver is approaching. Drivers need efficient routing.&lt;/p&gt;

&lt;p&gt;Our stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WebSocket gateway&lt;/strong&gt; for real-time client connections&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis Pub/Sub&lt;/strong&gt; for broadcasting location updates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostGIS&lt;/strong&gt; for geospatial queries (nearest driver, delivery radius)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Maps / Mapbox&lt;/strong&gt; for routing and ETA calculation
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified driver location update flow&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateDriverLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;driverId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Store in PostGIS for geospatial queries&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    UPDATE drivers 
    SET location = ST_SetSRID(ST_MakePoint($1, $2), 4326),
        updated_at = NOW()
    WHERE id = $3 AND tenant_id = $4
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;driverId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="c1"&gt;// Broadcast to connected clients via Redis Pub/Sub&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`location:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;driverId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Update active order ETAs&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;recalculateETAs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;driverId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Payment Processing for Brazil
&lt;/h3&gt;

&lt;p&gt;Payment in Brazil is unique. Pix (instant payment) now accounts for 40%+ of digital transactions. Credit card installments (parcelamento) are standard — customers expect to pay for a R$100 order in 3x of R$33.33.&lt;/p&gt;

&lt;p&gt;Our payment service handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pix&lt;/strong&gt; (instant, QR code or copy-paste)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credit/debit cards&lt;/strong&gt; via payment gateways (Mercado Pago, PagSeguro, Stripe Brazil)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Installments&lt;/strong&gt; (parcelamento) up to 12x&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cash on delivery&lt;/strong&gt; (still common in smaller cities)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vouchers and loyalty points&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each tenant can configure which payment methods they accept and set their own installment rules. The payment service abstracts gateway differences so tenant configuration is just feature flags.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling Challenges We Didn't Expect
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Friday Night Problem
&lt;/h3&gt;

&lt;p&gt;In delivery, traffic is not uniform. Friday and Saturday nights between 7-10 PM can see 10x the traffic of a Tuesday morning. For a multi-tenant platform, this means 30+ brands all spiking simultaneously.&lt;/p&gt;

&lt;p&gt;Our solution: Kubernetes Horizontal Pod Autoscaler (HPA) with custom metrics based on order volume per tenant, not just CPU. We also implemented tenant-level rate limiting to prevent a single brand's promotion from degrading service for everyone else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Menu Synchronization
&lt;/h3&gt;

&lt;p&gt;Restaurants change their menus constantly. Items go out of stock mid-shift. Prices change for promotions. Across 30+ brands with hundreds of restaurants each, menu sync becomes a distributed systems problem.&lt;/p&gt;

&lt;p&gt;We built a menu service with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Eventual consistency&lt;/strong&gt; model (menus sync within 30 seconds)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimistic UI&lt;/strong&gt; (customer sees menu, stock validated at order time)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhook-based&lt;/strong&gt; restaurant integrations for POS systems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache invalidation&lt;/strong&gt; strategy using Redis with tenant-namespaced keys&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Driver Assignment Algorithm
&lt;/h3&gt;

&lt;p&gt;Assigning drivers to orders is an optimization problem. You want to minimize delivery time, maximize driver utilization, and keep things fair. We use a scoring algorithm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;score = (proximity_weight × distance_score) 
      + (availability_weight × idle_time_score)
      + (performance_weight × rating_score)
      + (fairness_weight × orders_today_score)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Weights are configurable per tenant. Some brands prioritize speed (higher proximity weight), others prioritize fairness for drivers (higher fairness weight).&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature Flag Architecture
&lt;/h2&gt;

&lt;p&gt;With 30+ brands, each with different requirements, feature flags are essential. We use a hierarchical configuration system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"platform_defaults"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"max_delivery_radius_km"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enable_pix_payment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enable_installments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enable_cash_on_delivery"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enable_driver_tips"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enable_scheduled_orders"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tenant_overrides"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"brand-pharmacy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"enable_prescription_upload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"max_delivery_radius_km"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"enable_scheduled_orders"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"required_documents"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"prescription"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"brand-grocery"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"enable_item_substitution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"enable_weight_based_pricing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means a pharmacy brand gets prescription upload and larger delivery radius, while a grocery brand gets item substitution — all from the same codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring and Observability
&lt;/h2&gt;

&lt;p&gt;With 30+ tenants, when something breaks, you need to know WHICH tenant is affected and WHY. Our observability stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Distributed tracing&lt;/strong&gt; (Jaeger) with tenant_id in every span&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metrics&lt;/strong&gt; (Prometheus + Grafana) with tenant-level dashboards&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alerting&lt;/strong&gt; based on per-tenant SLOs (99.9% order success rate)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log aggregation&lt;/strong&gt; (ELK stack) with tenant-indexed logs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key insight: aggregate metrics hide problems. If overall order success rate is 99.5%, that sounds fine. But if one tenant has 95% success and the others have 99.9%, that one tenant is having a terrible experience. Per-tenant SLOs catch this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Business Metrics That Matter
&lt;/h2&gt;

&lt;p&gt;After 6+ years running this platform, here are the numbers that actually matter:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Time to onboard new brand&lt;/td&gt;
&lt;td&gt;2-4 weeks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average monthly orders per brand&lt;/td&gt;
&lt;td&gt;15K-50K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Platform uptime (2025)&lt;/td&gt;
&lt;td&gt;99.95%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost per brand (infrastructure)&lt;/td&gt;
&lt;td&gt;~$800-1,200/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Order processing latency (p95)&lt;/td&gt;
&lt;td&gt;&amp;lt; 200ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Driver assignment time (p95)&lt;/td&gt;
&lt;td&gt;&amp;lt; 3 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Lessons for Anyone Building Multi-Tenant Platforms
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Start multi-tenant from day one.&lt;/strong&gt; We didn't, and the migration cost us 4 months. If you know you'll have multiple tenants, build for it immediately.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;RLS is not optional.&lt;/strong&gt; Application-level tenant filtering will eventually fail. Database-level isolation (PostgreSQL RLS) is the only approach that scales safely.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Feature flags &amp;gt; code forks.&lt;/strong&gt; Never maintain separate codebases per tenant. Feature flags with a hierarchical config system handle 99% of customization needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Per-tenant observability is essential.&lt;/strong&gt; Aggregate metrics lie. Build dashboards that let you see each tenant's health independently.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cache namespacing saves lives.&lt;/strong&gt; Every cache key must include tenant_id. One cache poisoning incident across tenants, and you'll wish you'd done this from the start.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plan for traffic spikes.&lt;/strong&gt; In delivery, peak traffic is 10x baseline. Auto-scaling with custom metrics (not just CPU) is the only way to handle this reliably.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;We're currently exploring AI-powered features for the platform: demand prediction (so restaurants can prep ingredients before the rush), dynamic pricing optimization, and automated customer support through conversational AI.&lt;/p&gt;

&lt;p&gt;The delivery market in Brazil continues to grow, especially in cities outside the major metros. The white-label model is perfectly positioned for this expansion because it gives local entrepreneurs the technology they need without the cost of building from scratch.&lt;/p&gt;

&lt;p&gt;If you're building multi-tenant platforms — whether for delivery or any other vertical — the architectural principles are the same: strict data isolation, configurable feature flags, per-tenant observability, and infrastructure that scales with demand.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;José Gonçalves is the Founder of Mind Group Technologies, a software company based in Sorocaba, Brazil, building multi-tenant platforms for delivery, healthcare, fintech, and education. Learn more at &lt;a href="https://mindconsulting.com.br" rel="noopener noreferrer"&gt;mindconsulting.com.br&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>webdev</category>
      <category>programming</category>
      <category>devops</category>
    </item>
    <item>
      <title>From Monolith to Microservices: A Real Migration Story From a Brazilian Software Company</title>
      <dc:creator>José Gonçalves</dc:creator>
      <pubDate>Sun, 15 Feb 2026 13:18:09 +0000</pubDate>
      <link>https://dev.to/jos_gonalves_fac39f3437/from-monolith-to-microservices-a-real-migration-story-from-a-brazilian-software-company-28pm</link>
      <guid>https://dev.to/jos_gonalves_fac39f3437/from-monolith-to-microservices-a-real-migration-story-from-a-brazilian-software-company-28pm</guid>
      <description>&lt;p&gt;Two years ago, our monolithic application was killing us. Not metaphorically — it was literally costing us clients.&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://mindconsulting.com.br" rel="noopener noreferrer"&gt;Mind Group Technologies&lt;/a&gt;, we run a white-label SaaS platform that powers 30+ brands across delivery, fintech, healthcare, and education in Brazil and Latin America. By 2022, our monolith had grown into a 400K+ line codebase that took 45 minutes to deploy and crashed in ways that affected every single client simultaneously.&lt;/p&gt;

&lt;p&gt;This is the story of how we migrated to microservices — the decisions we got right, the mistakes we made, and the actual numbers from before and after.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why We Had to Move
&lt;/h2&gt;

&lt;p&gt;Our monolith wasn't always a problem. From 2016 to 2020, it was perfect. One codebase, one deployment, one team that understood everything. We could ship features fast because there was zero coordination overhead.&lt;/p&gt;

&lt;p&gt;The breaking point came at scale:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploy time&lt;/strong&gt;: 45 minutes for a full deployment. During that time, all 30+ brands experienced downtime or degraded performance. We were deploying during off-peak hours (2 AM BRT), which meant engineers working ungodly hours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blast radius&lt;/strong&gt;: A bug in the healthcare module once took down the delivery platforms. A memory leak in a reporting feature crashed the entire application. Every failure was a total failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Team bottlenecks&lt;/strong&gt;: With 15 engineers working on one codebase, merge conflicts were constant. Feature branches diverged for weeks. Code reviews became archaeological expeditions through unrelated changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scaling limitations&lt;/strong&gt;: Our fintech module needed 10x more compute during month-end reconciliation. But scaling the monolith meant scaling everything — including the education module that was barely used at night.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Database contention&lt;/strong&gt;: One massive PostgreSQL database serving all modules. Complex queries from the reporting module were slowing down real-time operations in the delivery module.&lt;/p&gt;

&lt;p&gt;The final straw: a client threatened to leave because our deployment windows conflicted with their peak business hours. We couldn't deploy without downtime, and we couldn't avoid deploying because features and fixes were piling up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Migration Plan
&lt;/h2&gt;

&lt;p&gt;We didn't do a big-bang rewrite. That's how companies die. Instead, we used the Strangler Fig Pattern — gradually extracting services from the monolith while keeping it running.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 1: Identify Service Boundaries (Month 1-2)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We mapped our monolith's functionality into domain boundaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auth Service&lt;/strong&gt;: Authentication, authorization, JWT management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tenant Service&lt;/strong&gt;: Tenant configuration, feature flags, branding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delivery Service&lt;/strong&gt;: Order management, driver dispatch, tracking&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fintech Service&lt;/strong&gt;: Transactions, reconciliation, KYC&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Healthcare Service&lt;/strong&gt;: Patient records, appointments, triage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Education Service&lt;/strong&gt;: Courses, progress tracking, assessments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notification Service&lt;/strong&gt;: Email, SMS, push notifications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reporting Service&lt;/strong&gt;: Analytics, dashboards, data exports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key insight: service boundaries should follow business domains, not technical layers. Don't create a "database service" or a "validation service." Create services that own a complete business capability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 2: Extract the First Service (Month 3-4)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We started with the Notification Service because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It had the clearest boundary (sends messages, receives events)&lt;/li&gt;
&lt;li&gt;Low risk — if notifications fail, the core business continues&lt;/li&gt;
&lt;li&gt;High value — it was one of the biggest performance bottlenecks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The extraction process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Built the new Notification Service as a standalone Node.js application&lt;/li&gt;
&lt;li&gt;Created a message queue (RabbitMQ) between the monolith and the new service&lt;/li&gt;
&lt;li&gt;Modified the monolith to publish events instead of sending notifications directly&lt;/li&gt;
&lt;li&gt;Deployed both systems in parallel, with the monolith as the primary&lt;/li&gt;
&lt;li&gt;Gradually shifted traffic to the new service&lt;/li&gt;
&lt;li&gt;Removed notification code from the monolith
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Before: Monolith sends notifications directly
async function createOrder(orderData) {
  const order = await db.orders.create(orderData);
  await sendEmail(order.customer.email, 'Order Confirmed', template);
  await sendSMS(order.customer.phone, 'Your order is confirmed');
  await sendPush(order.customer.deviceToken, 'Order Confirmed');
  return order;
}

// After: Monolith publishes event, service handles notifications
async function createOrder(orderData) {
  const order = await db.orders.create(orderData);
  await messageQueue.publish('order.created', {
    orderId: order.id,
    tenantId: order.tenantId,
    customerEmail: order.customer.email,
    customerPhone: order.customer.phone,
    deviceToken: order.customer.deviceToken
  });
  return order;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Phase 3: Extract High-Value Services (Month 5-12)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After Notification, we extracted in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Auth Service&lt;/strong&gt; — critical for security isolation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tenant Service&lt;/strong&gt; — foundation for multi-tenant microservices&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fintech Service&lt;/strong&gt; — needed independent scaling for month-end&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delivery Service&lt;/strong&gt; — needed independent scaling for lunch rush&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Healthcare Service&lt;/strong&gt; — needed strict LGPD compliance isolation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Education Service&lt;/strong&gt; — lowest traffic, extracted last&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Phase 4: Kill the Monolith (Month 13-18)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The remaining monolith became a thin API gateway that routed requests to the appropriate service. Eventually, we replaced it with a proper API gateway (Kong) and the monolith was officially retired.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Stack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Services&lt;/strong&gt;: Node.js (Express) for API services, Go for high-performance services (delivery tracking, real-time notifications)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Communication&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Synchronous: gRPC for service-to-service calls that need immediate responses&lt;/li&gt;
&lt;li&gt;Asynchronous: RabbitMQ for event-driven communication (order created, payment processed, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Database&lt;/strong&gt;: Each service owns its own PostgreSQL database. No shared databases. The Fintech service also uses Redis for real-time transaction caching.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deployment&lt;/strong&gt;: Kubernetes (EKS on AWS). Each service runs as a separate deployment with independent scaling policies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service Mesh&lt;/strong&gt;: Initially none — we added Istio after reaching 8 services because managing service-to-service communication, retries, and circuit breakers at the application level was becoming unsustainable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observability&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Distributed tracing: Jaeger (every request gets a trace ID that flows through all services)&lt;/li&gt;
&lt;li&gt;Logging: Structured JSON logs → CloudWatch → Elasticsearch&lt;/li&gt;
&lt;li&gt;Metrics: Prometheus + Grafana&lt;/li&gt;
&lt;li&gt;Alerting: PagerDuty for critical alerts, Slack for warnings&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Numbers: Before vs After
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Monolith&lt;/th&gt;
&lt;th&gt;Microservices&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Deploy time&lt;/td&gt;
&lt;td&gt;45 min (full app)&lt;/td&gt;
&lt;td&gt;3-5 min (per service)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deploy frequency&lt;/td&gt;
&lt;td&gt;2x/week&lt;/td&gt;
&lt;td&gt;10-15x/week&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blast radius&lt;/td&gt;
&lt;td&gt;All 30+ brands&lt;/td&gt;
&lt;td&gt;Only affected service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scale granularity&lt;/td&gt;
&lt;td&gt;All or nothing&lt;/td&gt;
&lt;td&gt;Per-service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mean Time to Recovery&lt;/td&gt;
&lt;td&gt;30-60 min&lt;/td&gt;
&lt;td&gt;5-10 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monthly infrastructure cost&lt;/td&gt;
&lt;td&gt;$12K&lt;/td&gt;
&lt;td&gt;$18K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Developer velocity (PRs/week)&lt;/td&gt;
&lt;td&gt;~25&lt;/td&gt;
&lt;td&gt;~45&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The infrastructure cost went UP. This is the dirty secret of microservices that nobody tells you. Running Kubernetes, a service mesh, distributed tracing, and multiple databases costs more than a single server. But the business value — faster deploys, independent scaling, fault isolation — more than compensated.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mistakes We Made
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Extracting too many services at once&lt;/strong&gt;&lt;br&gt;
In Month 5, we tried extracting Auth, Tenant, and Fintech simultaneously. Three major surgeries on a running system. We had cascading failures for two weeks. Lesson: one service extraction at a time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Distributed transactions&lt;/strong&gt;&lt;br&gt;
We underestimated how hard distributed transactions would be. When a fintech payment requires updating the order status in the delivery service AND creating an audit log in the compliance service, you need a saga pattern or eventual consistency. We spent a month building a saga orchestrator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Data duplication anxiety&lt;/strong&gt;&lt;br&gt;
We initially tried to avoid duplicating data across services by calling other services for every piece of data. This created a web of synchronous dependencies that was worse than the monolith. Eventually we embraced data duplication — each service stores the data it needs, synced via events.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Skipping contract testing&lt;/strong&gt;&lt;br&gt;
Without contract testing, Service A would change its API response format without telling Service B. Things would break in production because our integration tests didn't cover every cross-service interaction. We adopted Pact for contract testing and it eliminated this class of bugs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Over-engineering from day one&lt;/strong&gt;&lt;br&gt;
We added Istio (service mesh) before we needed it. For 3-4 services, simple HTTP with retry logic is fine. We should have waited until the complexity justified the operational overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We'd Do Differently
&lt;/h2&gt;

&lt;p&gt;If we started the migration today:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Start with an API gateway immediately&lt;/strong&gt; — even before extracting the first service. This gives you a single entry point and makes routing changes invisible to clients.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Invest in a shared library for cross-cutting concerns&lt;/strong&gt; — tenant context, logging, tracing, auth validation. Every service needs these, and building them independently leads to inconsistency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use event sourcing for the fintech module&lt;/strong&gt; — instead of traditional CRUD, event sourcing would have given us a natural audit trail and made distributed transactions simpler.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hire a platform engineer earlier&lt;/strong&gt; — we treated infrastructure as everyone's responsibility, which meant it was no one's responsibility. A dedicated platform engineer from Month 1 would have saved us months of debugging Kubernetes configs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Better staging environments&lt;/strong&gt; — our staging environment didn't replicate the multi-service topology accurately. Bugs that appeared in production weren't reproducible in staging. We now use namespaces in Kubernetes to create per-developer staging environments.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Is Microservices Right for You?
&lt;/h2&gt;

&lt;p&gt;Honest answer: probably not, unless you have these conditions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multiple teams&lt;/strong&gt; (4+ engineers minimum) working on the same codebase&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Different scaling requirements&lt;/strong&gt; across modules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Independent deployment needs&lt;/strong&gt; (can't afford whole-system downtime)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regulatory requirements&lt;/strong&gt; that demand service isolation (healthcare, fintech)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're a team of 3 building an MVP, stay monolithic. A well-structured monolith with clear module boundaries is far better than premature microservices. We only needed microservices because we had 30+ brands depending on the same system with conflicting requirements.&lt;/p&gt;

&lt;p&gt;The monolith served Mind Group Technologies well for four years. Microservices have served us well for the next four. The architecture should match your scale, not your ambition.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;José Gonçalves is the Founder of &lt;a href="https://mindconsulting.com.br" rel="noopener noreferrer"&gt;Mind Group Technologies&lt;/a&gt;, a software company based in Sorocaba, SP, Brazil. Mind Group builds multi-tenant SaaS platforms serving 30+ brands across healthcare, fintech, delivery, and education.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>webdev</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Managing Remote Dev Teams Across Brazil: What We Learned After 8 Years</title>
      <dc:creator>José Gonçalves</dc:creator>
      <pubDate>Sun, 15 Feb 2026 13:14:12 +0000</pubDate>
      <link>https://dev.to/jos_gonalves_fac39f3437/managing-remote-dev-teams-across-brazil-what-we-learned-after-8-years-2fl3</link>
      <guid>https://dev.to/jos_gonalves_fac39f3437/managing-remote-dev-teams-across-brazil-what-we-learned-after-8-years-2fl3</guid>
      <description>&lt;p&gt;In 2016, when I founded &lt;a href="https://mindconsulting.com.br" rel="noopener noreferrer"&gt;Mind Group Technologies&lt;/a&gt; in Sorocaba, São Paulo, everyone was in the same room. Five engineers, one whiteboard, zero Slack channels. We could solve problems by turning around in our chairs.&lt;/p&gt;

&lt;p&gt;Today, we have team members scattered across Brazil — from Sorocaba to Recife, Porto Alegre to Manaus. We serve clients in São Paulo, Rio de Janeiro, and increasingly, the United States and Europe. The office still exists, but it's optional.&lt;/p&gt;

&lt;p&gt;This transformation wasn't planned. COVID forced it, like it did for everyone. But what happened next was the interesting part: we discovered that remote work didn't just maintain our productivity — it improved it. And then we spent four years figuring out &lt;em&gt;why&lt;/em&gt; it worked and how to make it work better.&lt;/p&gt;

&lt;p&gt;Here's what we learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Brazil-Specific Challenges Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;Most "remote work advice" articles are written from a San Francisco perspective. They assume reliable gigabit internet, a quiet home office, and a culture where asynchronous communication is the default.&lt;/p&gt;

&lt;p&gt;Brazil is different.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Internet reliability varies wildly.&lt;/strong&gt; A developer in São Paulo might have 500Mbps fiber. A developer in a smaller city in Bahia might have 50Mbps that drops during rainstorms. We learned to design our workflows around intermittent connectivity — not as an edge case, but as a baseline assumption.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time zones within Brazil matter.&lt;/strong&gt; Brazil spans four time zones. When it's 9 AM in Sorocaba (BRT), it's 8 AM in Manaus (AMT). That one-hour difference sounds trivial until you're scheduling daily standups for a team spread across the country.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Home office infrastructure isn't equal.&lt;/strong&gt; Some team members have dedicated home offices. Others work from a bedroom shared with family members. We learned early that providing equipment stipends — monitors, chairs, headsets — isn't a perk; it's a productivity requirement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cultural communication styles differ by region.&lt;/strong&gt; Developers from São Paulo tend to be direct and fast-paced. Developers from the Northeast tend to be more relationship-oriented in communication. Neither style is better, but misreading communication patterns causes friction in remote teams.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our Remote Work Stack
&lt;/h2&gt;

&lt;p&gt;After years of experimentation, here's what actually works for us at Mind Group Technologies:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Communication:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Slack&lt;/strong&gt; for real-time chat, organized by project channels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Meet&lt;/strong&gt; for video calls (we tried Zoom, Teams, and Discord — Meet won for reliability in Brazil)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loom&lt;/strong&gt; for async video updates — a developer can record a 3-minute walkthrough of their PR instead of scheduling a meeting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Project Management:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Linear&lt;/strong&gt; for issue tracking (migrated from Jira — the speed difference is night and day)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notion&lt;/strong&gt; for documentation, wikis, and decision logs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Projects&lt;/strong&gt; for sprint boards tied directly to repositories&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Development:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt; for code, PRs, and code reviews&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VS Code + Live Share&lt;/strong&gt; for pair programming sessions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt; for consistent dev environments across machines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The rule we follow:&lt;/strong&gt; Every tool must work well on unstable connections. If a tool requires constant high-bandwidth connectivity, we don't use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Meetings That Actually Matter
&lt;/h2&gt;

&lt;p&gt;We killed most of our meetings. Here's what survived:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Daily Standup (15 min, async-first)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We use a Slack bot that asks three questions at 9 AM BRT:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What did you finish yesterday?&lt;/li&gt;
&lt;li&gt;What are you working on today?&lt;/li&gt;
&lt;li&gt;Any blockers?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Team members respond in text. If someone has a blocker, we spin up a quick call to resolve it. No standing meetings for status updates — those are a waste of synchronous time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Weekly Technical Review (45 min, video)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every Friday, one team member presents something technical: an architecture decision, a debugging story, a new tool evaluation. This serves two purposes: knowledge sharing and team bonding. Remote teams need rituals that aren't just about tickets and deadlines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Sprint Planning (1 hour, video, biweekly)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We plan two-week sprints. The product owner presents priorities, the team estimates, and we commit to a scope. This is the one meeting that absolutely must be synchronous because real-time negotiation about scope is difficult to do asynchronously.&lt;/p&gt;

&lt;p&gt;Everything else is async. Code reviews happen in GitHub. Design discussions happen in Notion documents with comments. Architecture decisions are documented in ADRs (Architecture Decision Records) that anyone can review and comment on within 48 hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Review as a Team-Building Tool
&lt;/h2&gt;

&lt;p&gt;In a remote team, code reviews are your primary touchpoint for technical mentorship. We treat them seriously:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every PR gets reviewed within 4 hours.&lt;/strong&gt; Not approved — reviewed. A first pass with comments. This keeps the development cycle moving and shows respect for the author's time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reviews are educational, not gatekeeping.&lt;/strong&gt; We write comments that explain the &lt;em&gt;why&lt;/em&gt; behind suggestions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Instead of: "Don't do this"
// We write: "Consider using a Map here instead of an 
// object because we need O(1) lookups and the keys 
// aren't always strings. See MDN docs: [link]"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Junior developers review senior code.&lt;/strong&gt; This sounds counterintuitive, but juniors asking questions about senior code is one of the best learning mechanisms. The senior has to explain their reasoning, and the junior learns architectural thinking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We use PR templates&lt;/strong&gt; that require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Description of what changed and why&lt;/li&gt;
&lt;li&gt;How to test locally&lt;/li&gt;
&lt;li&gt;Screenshots/recordings for UI changes&lt;/li&gt;
&lt;li&gt;Link to the related Linear issue&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Handling the "Isolation Problem"
&lt;/h2&gt;

&lt;p&gt;The biggest risk with remote teams isn't productivity — it's isolation. Developers who feel disconnected stop caring about the team's goals and start optimizing for their own comfort.&lt;/p&gt;

&lt;p&gt;Here's how we fight it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Virtual coffee chats.&lt;/strong&gt; Every week, a bot randomly pairs two team members for a 15-minute casual video call. No agenda, no work topics. Just two humans talking. It sounds forced, and it is — but it works. Relationships that would form naturally in an office need artificial scaffolding in a remote environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In-person meetups twice a year.&lt;/strong&gt; We bring the entire team to Sorocaba for a week. Three days of workshops and planning, two days of just hanging out. The cost is significant (flights from Manaus or Recife aren't cheap), but the team cohesion boost lasts months.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transparent salaries and growth paths.&lt;/strong&gt; Nothing breeds resentment in remote teams faster than information asymmetry. Everyone at Mind Group knows the salary bands for their level and what they need to do to reach the next level. This transparency eliminates the "am I being treated fairly?" anxiety that festers in remote environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Public recognition.&lt;/strong&gt; When someone does exceptional work, we recognize it publicly in Slack. Not vague praise — specific recognition: "Maria's refactoring of the payment module reduced API response times by 40% and made the codebase significantly more maintainable." Specific recognition shows that leadership is paying attention, even from a distance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hiring Remote: What We Look For
&lt;/h2&gt;

&lt;p&gt;Not every developer thrives in a remote environment. After hiring 50+ remote team members, here are the traits that predict success:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Written communication skills.&lt;/strong&gt; In a remote team, your ability to write clear messages, documentation, and PR descriptions is as important as your coding ability. We ask candidates to write a technical document as part of the interview process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Self-direction.&lt;/strong&gt; Remote developers need to identify what needs to be done next without constant guidance. We test this by giving candidates a deliberately ambiguous problem and seeing how they ask clarifying questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proactive communication.&lt;/strong&gt; The best remote developers over-communicate, not under-communicate. If something is taking longer than expected, they say so early. If they're stuck, they ask for help within hours, not days.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Comfort with async.&lt;/strong&gt; Some developers need immediate responses to every question. That doesn't work when your teammate is in a different time zone or deep in focused work. We look for people who can queue their questions and continue working on something else while waiting.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Productivity Data
&lt;/h2&gt;

&lt;p&gt;We've been tracking productivity metrics for four years across remote and in-office periods. Here's what the data shows:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PRs merged per developer per week:&lt;/strong&gt; Remote developers merge 15-20% more PRs than in-office developers. The likely explanation: fewer interruptions and no commute means more focused coding time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bug rate:&lt;/strong&gt; No statistically significant difference between remote and in-office periods. Code quality is maintained by our review process, not by physical proximity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time to resolve blockers:&lt;/strong&gt; 10-15% slower for remote teams. Async communication adds latency when someone is blocked. We've mitigated this with the "4-hour review" rule and designated "available for questions" time slots.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Employee retention:&lt;/strong&gt; Significantly better since going remote. Our annual turnover dropped from ~25% (industry average in Brazil) to ~12%. Developers value the flexibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Doesn't Work
&lt;/h2&gt;

&lt;p&gt;Being honest about failures:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fully async doesn't work for complex problem-solving.&lt;/strong&gt; When you need to make a high-stakes architectural decision, real-time video with screen sharing is irreplaceable. Don't try to do everything async.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Optional" cameras-on policies mean cameras are always off.&lt;/strong&gt; If you want face-to-face connection, make specific meetings camera-on by default. Don't leave it optional — social pressure will always trend toward cameras off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remote-first isn't remote-only.&lt;/strong&gt; Some tasks benefit from physical co-location: onboarding new team members, resolving interpersonal conflicts, brainstorming product direction. Our twice-yearly meetups exist because pure remote isn't optimal for everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitoring tools destroy trust.&lt;/strong&gt; We tried time-tracking and screen monitoring tools early on. They created an adversarial dynamic and the best developers threatened to quit. We removed them within a month. Trust your team or don't hire them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Managing remote dev teams in Brazil requires understanding the country's specific infrastructure, cultural, and economic context. What works in Silicon Valley doesn't automatically work in São Paulo, let alone in Manaus or Recife.&lt;/p&gt;

&lt;p&gt;The fundamentals: invest in async communication, design workflows for variable internet quality, create artificial social structures to replace office serendipity, and trust your developers to manage their own time.&lt;/p&gt;

&lt;p&gt;At Mind Group Technologies, remote work isn't a policy — it's our operating system. After eight years, we're convinced that Brazilian tech companies that master remote work will outcompete those that don't, simply because the talent pool expands from one city to an entire country of 215 million people.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;José Gonçalves is the Founder of &lt;a href="https://mindconsulting.com.br" rel="noopener noreferrer"&gt;Mind Group Technologies&lt;/a&gt;, a software company based in Sorocaba, SP, Brazil. Mind Group builds technology solutions across healthcare, fintech, and SaaS with a distributed team across Brazil.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>remote</category>
      <category>career</category>
      <category>productivity</category>
    </item>
    <item>
      <title>LGPD for Developers: What Brazil's Data Protection Law Actually Means for Your Code</title>
      <dc:creator>José Gonçalves</dc:creator>
      <pubDate>Sun, 15 Feb 2026 13:10:40 +0000</pubDate>
      <link>https://dev.to/jos_gonalves_fac39f3437/lgpd-for-developers-what-brazils-data-protection-law-actually-means-for-your-code-3dif</link>
      <guid>https://dev.to/jos_gonalves_fac39f3437/lgpd-for-developers-what-brazils-data-protection-law-actually-means-for-your-code-3dif</guid>
      <description>&lt;p&gt;If you're building software that touches Brazilian users' data, you need to understand LGPD. Not as a legal checkbox — as an architectural constraint that shapes how you write code.&lt;/p&gt;

&lt;p&gt;Brazil's Lei Geral de Proteção de Dados (LGPD), enacted in 2020, is often compared to GDPR. That comparison is accurate but incomplete. LGPD has its own nuances, its own enforcement patterns, and its own practical implications for developers working in the Brazilian market.&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://mindconsulting.com.br" rel="noopener noreferrer"&gt;Mind Group Technologies&lt;/a&gt;, we've been building LGPD-compliant systems since the law was enacted. We work across healthcare, fintech, and SaaS — three verticals where data protection isn't optional; it's existential. Here's what we've learned about translating legal requirements into actual code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Principles (In Developer Terms)
&lt;/h2&gt;

&lt;p&gt;LGPD has 10 legal bases for processing personal data. But in practice, most developers need to think about three things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Purpose Limitation&lt;/strong&gt;&lt;br&gt;
You can only collect data for a specific, declared purpose. In code terms: if your user signs up to receive a newsletter, you can't use their email for marketing campaigns without separate consent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// BAD: Collecting data without clear purpose
const userData = {
  name: req.body.name,
  email: req.body.email,
  cpf: req.body.cpf,        // Why do you need CPF for a newsletter?
  phone: req.body.phone,    // Why do you need phone for a newsletter?
  address: req.body.address  // Definitely don't need this
};

// GOOD: Data minimization
const userData = {
  email: req.body.email,
  consent_newsletter: true,
  consent_timestamp: new Date().toISOString(),
  consent_version: 'v2.1'
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Data Minimization&lt;/strong&gt;&lt;br&gt;
Collect only what you need. This sounds obvious but watch how many registration forms ask for CPF, full address, and phone number when all they need is an email.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Right to Deletion&lt;/strong&gt;&lt;br&gt;
Users can request that you delete their data. This means your database schema needs to support actual deletion — not just soft deletes that leave data in your tables forever.&lt;/p&gt;
&lt;h2&gt;
  
  
  Consent Management: The Technical Implementation
&lt;/h2&gt;

&lt;p&gt;LGPD requires explicit, granular consent. Here's what that looks like in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Consent record structure&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;consentRecord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uuid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;consents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;purpose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email_marketing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;granted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2026-02-13T10:30:00Z&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;privacy_policy_v3.2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;checkbox_signup_form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;189.x.x.x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;purpose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;analytics_tracking&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;granted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2026-02-13T10:30:00Z&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;privacy_policy_v3.2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cookie_banner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;189.x.x.x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key implementation details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Versioned consent&lt;/strong&gt;: When your privacy policy changes, previous consents need to be re-obtained. Track which version each consent was given under.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Granular opt-in&lt;/strong&gt;: Don't use a single "I agree to everything" checkbox. Each purpose needs its own consent toggle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit trail&lt;/strong&gt;: Every consent grant and revocation must be logged with timestamp, method, and context.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Deletion Problem: Harder Than You Think
&lt;/h2&gt;

&lt;p&gt;When a user requests data deletion, you need to delete their data from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your primary database&lt;/li&gt;
&lt;li&gt;All replicas and read replicas&lt;/li&gt;
&lt;li&gt;Backup systems (within reasonable timeframe)&lt;/li&gt;
&lt;li&gt;Log files (PII should never be in logs)&lt;/li&gt;
&lt;li&gt;Third-party services you've shared data with&lt;/li&gt;
&lt;li&gt;Cache layers (Redis, CDN)&lt;/li&gt;
&lt;li&gt;Search indexes (Elasticsearch, Algolia)&lt;/li&gt;
&lt;li&gt;Analytics systems&lt;/li&gt;
&lt;li&gt;Email marketing platforms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At Mind Group Technologies, we built a deletion pipeline that handles this systematically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Request → Validation → Primary DB Delete → 
Queue Async Jobs → [Cache Purge, Search Index Update, 
Third-Party API Calls, Log Scrubbing] → 
Confirmation Email → Audit Log Entry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The async jobs are critical. You can't wait for Elasticsearch to re-index and your email provider to process the deletion before responding to the user. Queue these as background jobs with retry logic and dead-letter queues for failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Encryption: At Rest and In Transit
&lt;/h2&gt;

&lt;p&gt;LGPD doesn't specify encryption algorithms, but the ANPD (Brazil's data protection authority) has made it clear that encryption is expected for sensitive data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;At rest&lt;/strong&gt;: AES-256 for database fields containing PII. Don't encrypt everything — that kills query performance. Encrypt sensitive fields: CPF, health records, financial data, biometric data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In transit&lt;/strong&gt;: TLS 1.2+ for all API communications. This should be table stakes in 2026, but you'd be surprised how many internal services still communicate over plain HTTP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key management&lt;/strong&gt;: Don't store encryption keys in the same database as the encrypted data. Use AWS KMS, HashiCorp Vault, or similar. Rotate keys periodically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Healthcare and Fintech: Extra Requirements
&lt;/h2&gt;

&lt;p&gt;If you're building in healthcare or fintech, LGPD's "sensitive data" provisions add additional layers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Healthcare&lt;/strong&gt;: Patient data (health records, diagnoses, prescriptions) is classified as sensitive personal data. Processing requires explicit consent AND a legitimate purpose. At Mind Group, our healthcare systems implement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Field-level encryption for diagnoses and prescriptions&lt;/li&gt;
&lt;li&gt;Role-based access (doctors see clinical data; billing sees only billing data)&lt;/li&gt;
&lt;li&gt;Automatic data retention policies (some records must be kept for 20 years; others should be purged after treatment)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fintech&lt;/strong&gt;: Financial transaction data requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Immutable audit logs (append-only tables)&lt;/li&gt;
&lt;li&gt;Transaction-level access controls&lt;/li&gt;
&lt;li&gt;PCI-DSS compliance layered on top of LGPD&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Mistakes We See
&lt;/h2&gt;

&lt;p&gt;After eight years building compliant systems at Mind Group Technologies, here are the most common LGPD mistakes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Logging PII&lt;/strong&gt;: Your application logs should NEVER contain CPF, email addresses, or any PII. Use UUIDs for log correlation. Scrub PII from error messages before they reach your logging system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Storing consent in cookies&lt;/strong&gt;: Cookies are not a valid consent record. Cookies expire, get cleared, and can't be audited. Store consent in your database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ignoring third-party data flows&lt;/strong&gt;: When you send user data to an analytics platform, an email provider, or a payment processor, YOU are still responsible for that data under LGPD. Map all your data flows.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;"Soft delete" instead of actual deletion&lt;/strong&gt;: When a user requests deletion, a &lt;code&gt;deleted_at&lt;/code&gt; timestamp is not enough. The data needs to be actually removed or anonymized beyond recovery.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No data processing inventory&lt;/strong&gt;: LGPD requires you to know WHAT data you collect, WHERE it's stored, WHO has access, and WHY. Build a data map before you need one.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Practical Checklist for Developers
&lt;/h2&gt;

&lt;p&gt;If you're building a new application for the Brazilian market:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Implement granular consent management with versioning&lt;/li&gt;
&lt;li&gt;[ ] Add field-level encryption for sensitive PII&lt;/li&gt;
&lt;li&gt;[ ] Build a data deletion pipeline (primary DB + all downstream systems)&lt;/li&gt;
&lt;li&gt;[ ] Remove PII from all log outputs&lt;/li&gt;
&lt;li&gt;[ ] Map all third-party data flows&lt;/li&gt;
&lt;li&gt;[ ] Implement role-based access controls&lt;/li&gt;
&lt;li&gt;[ ] Set up data retention policies with automated enforcement&lt;/li&gt;
&lt;li&gt;[ ] Create a data processing inventory document&lt;/li&gt;
&lt;li&gt;[ ] Add cookie consent banner with reject option&lt;/li&gt;
&lt;li&gt;[ ] Implement right to data portability (export user data as JSON/CSV)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Business Case for Compliance
&lt;/h2&gt;

&lt;p&gt;LGPD fines can reach 2% of revenue (up to R$50 million per violation). But the real cost of non-compliance is losing enterprise clients who require LGPD compliance from their vendors.&lt;/p&gt;

&lt;p&gt;At Mind Group Technologies, our LGPD compliance has become a competitive advantage. When we pitch to healthcare companies, hospitals, and fintech firms, being able to demonstrate compliant architecture from day one closes deals faster than any feature demo.&lt;/p&gt;

&lt;p&gt;If you're building for the Brazilian market, treat LGPD as a feature, not a burden. Your future enterprise customers will thank you.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;José Gonçalves is the Founder of &lt;a href="https://mindconsulting.com.br" rel="noopener noreferrer"&gt;Mind Group Technologies&lt;/a&gt;, a software company based in Sorocaba, São Paulo, Brazil. Mind Group builds LGPD-compliant systems across healthcare, fintech, and SaaS verticals.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>We Built One Platform That Powers 30+ Brands — The White-Label SaaS Playbook</title>
      <dc:creator>José Gonçalves</dc:creator>
      <pubDate>Fri, 13 Feb 2026 18:10:47 +0000</pubDate>
      <link>https://dev.to/jos_gonalves_fac39f3437/we-built-one-platform-that-powers-30-brands-the-white-label-saas-playbook-445d</link>
      <guid>https://dev.to/jos_gonalves_fac39f3437/we-built-one-platform-that-powers-30-brands-the-white-label-saas-playbook-445d</guid>
      <description>&lt;p&gt;Three years ago, a client came to us with a problem: they had built a successful delivery management platform for their own logistics company, and three of their partners wanted the same system — but branded as their own.&lt;/p&gt;

&lt;p&gt;"Can you just copy the code and change the logo?" they asked.&lt;/p&gt;

&lt;p&gt;The answer was no. But the question led us to build something far more valuable: a &lt;strong&gt;multi-tenant, white-label SaaS architecture&lt;/strong&gt; that now powers 30+ brands from a single codebase.&lt;/p&gt;

&lt;p&gt;At &lt;strong&gt;&lt;a href="https://mindconsulting.com.br" rel="noopener noreferrer"&gt;Mind Group Technologies&lt;/a&gt;&lt;/strong&gt;, this project became one of our most technically interesting and commercially successful. Here's what we learned building it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why White-Label?
&lt;/h2&gt;

&lt;p&gt;The SaaS market globally is worth over &lt;strong&gt;$200 billion&lt;/strong&gt;. In Latin America, it's growing at &lt;strong&gt;35-40% year-over-year&lt;/strong&gt;. But here's the insight most SaaS founders miss: &lt;strong&gt;you don't always need to build a brand to build a business&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;White-label SaaS lets you build one platform and sell it as many products. Your clients get a fully branded solution. You get recurring revenue from a single codebase.&lt;/p&gt;

&lt;p&gt;The economics are compelling: instead of acquiring end-users one by one, you acquire business clients who bring their entire user base with them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Multi-Tenancy: The Foundation
&lt;/h3&gt;

&lt;p&gt;Every white-label platform needs a robust multi-tenancy model. We chose a &lt;strong&gt;shared database with tenant isolation&lt;/strong&gt; approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────┐
│           Application Layer              │
│  ┌──────┐  ┌──────┐  ┌──────┐          │
│  │Brand A│  │Brand B│  │Brand C│          │
│  └──┬───┘  └──┬───┘  └──┬───┘          │
│     │         │         │                │
│  ┌──▼─────────▼─────────▼──┐            │
│  │    Tenant Router         │            │
│  └──────────┬──────────────┘            │
│             │                            │
│  ┌──────────▼──────────────┐            │
│  │   Shared Database        │            │
│  │   (Row-Level Security)   │            │
│  └─────────────────────────┘            │
└─────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why shared database?&lt;/strong&gt; For white-label with 30+ tenants, maintaining separate databases per tenant becomes an operational nightmare. Shared database with row-level security (using PostgreSQL RLS) gives us data isolation without the infrastructure overhead.&lt;/p&gt;

&lt;p&gt;The key implementation details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Tenant middleware - runs on every request&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tenantMiddleware&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tenantId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extractTenantFromDomain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tenant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getTenantConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&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="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unknown tenant&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tenant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createTenantContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Sets RLS context&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Theming Engine
&lt;/h3&gt;

&lt;p&gt;Each brand needs to look completely different. We built a theming engine that handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Colors&lt;/strong&gt; — Primary, secondary, accent, backgrounds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typography&lt;/strong&gt; — Font families, sizes, weights&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logo and imagery&lt;/strong&gt; — Header, footer, favicon, email templates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layout variations&lt;/strong&gt; — Some brands want sidebar nav, others want top nav&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom CSS&lt;/strong&gt; — For clients who want pixel-perfect customization
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Theme configuration per tenant&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;themeConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;brandA&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;primaryColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#1a73e8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fontFamily&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Inter, sans-serif&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;logoUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/assets/brandA/logo.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sidebar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;customCSS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.header { border-bottom: 3px solid #1a73e8; }&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;brandB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;primaryColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#e53935&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fontFamily&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Roboto, sans-serif&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;logoUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/assets/brandB/logo.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;topnav&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;customCSS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Feature Flags Per Tenant
&lt;/h3&gt;

&lt;p&gt;Not every brand gets every feature. We use a feature flag system tied to the tenant's subscription tier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canAccessFeature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tierFeatures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;basic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reports&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;pro&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reports&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;analytics&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;enterprise&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reports&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;analytics&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;whitelabel-emails&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom-domain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tierFeatures&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tier&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-World Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Delivery Management
&lt;/h3&gt;

&lt;p&gt;Our original use case: a delivery platform white-labeled for logistics companies. Each company sees their own brand, their own data, their own customer portal. But under the hood, it's one platform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Results:&lt;/strong&gt; 30+ brands, &lt;strong&gt;60-70% infrastructure savings&lt;/strong&gt; compared to individual deployments. One bug fix benefits all clients simultaneously.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fintech Dashboards
&lt;/h3&gt;

&lt;p&gt;We extended the pattern to financial dashboards. Banks and fintechs get a fully branded analytics platform for their customers. Each bank's branding, each bank's compliance requirements, one shared platform.&lt;/p&gt;

&lt;p&gt;At &lt;strong&gt;Mind Group Technologies&lt;/strong&gt;, we built the compliance layer once (LGPD, BCB regulations) and every tenant benefits. That's the power of shared infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Healthcare Portals
&lt;/h3&gt;

&lt;p&gt;Patient portals for clinic networks. Each clinic has their own branded portal where patients schedule appointments, view results, and communicate with providers. The multi-tenant architecture means adding a new clinic takes hours, not weeks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Education LMS
&lt;/h3&gt;

&lt;p&gt;Learning management systems for training companies. Each company gets their own branded LMS with courses, certifications, and progress tracking. Content is shared or isolated depending on the licensing agreement.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Challenges We Solved
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Data Isolation is Non-Negotiable
&lt;/h3&gt;

&lt;p&gt;In healthcare and fintech white-label products, data leakage between tenants is catastrophic. We use PostgreSQL Row-Level Security (RLS) as the last line of defense:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Enable RLS on the orders table&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;ENABLE&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt; &lt;span class="k"&gt;LEVEL&lt;/span&gt; &lt;span class="k"&gt;SECURITY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Create policy for tenant isolation&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;POLICY&lt;/span&gt; &lt;span class="n"&gt;tenant_isolation&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
  &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.current_tenant'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if application code has a bug, the database itself prevents cross-tenant data access.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Performance at Scale
&lt;/h3&gt;

&lt;p&gt;With 30+ tenants sharing a database, query performance matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tenant-partitioned indexes&lt;/strong&gt; on all frequently queried tables&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection pooling&lt;/strong&gt; with PgBouncer configured per-tenant&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching layer&lt;/strong&gt; (Redis) with tenant-prefixed keys&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read replicas&lt;/strong&gt; for analytics-heavy tenants&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Deployment Without Downtime
&lt;/h3&gt;

&lt;p&gt;All tenants share the same codebase, so deployments affect everyone. We use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blue-green deployments&lt;/strong&gt; for zero-downtime releases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature flags&lt;/strong&gt; to gradually roll out changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canary releases&lt;/strong&gt; — new features go to one tenant first&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant rollback&lt;/strong&gt; capability if issues are detected&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Custom Domain Routing
&lt;/h3&gt;

&lt;p&gt;Each tenant can use their own domain. The routing layer maps incoming requests to the correct tenant configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.brandA.com → Tenant A config
logistics.brandB.io → Tenant B config
portal.brandC.com.br → Tenant C config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We handle SSL certificates automatically using Let's Encrypt with DNS validation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Business Model
&lt;/h2&gt;

&lt;p&gt;White-label SaaS typically follows a &lt;strong&gt;B2B2C model&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;You&lt;/strong&gt; build and maintain the platform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your clients&lt;/strong&gt; (businesses) brand and sell it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Their customers&lt;/strong&gt; (end-users) use the product&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Revenue comes from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Monthly SaaS fees&lt;/strong&gt; per tenant (tiered by features/users)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Setup fees&lt;/strong&gt; for onboarding and customization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revenue share&lt;/strong&gt; on transactions processed through the platform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Professional services&lt;/strong&gt; for custom integrations&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Start with one use case, then generalize.&lt;/strong&gt; Our delivery platform became white-label because a real client needed it. We didn't build "a white-label platform" — we made an existing product configurable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Invest in admin tooling early.&lt;/strong&gt; The internal dashboard for managing tenants, configurations, and deployments saves more time than any customer-facing feature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test with the ugliest theme.&lt;/strong&gt; If your platform looks good with neon green and comic sans, it'll look good with anything. We literally test with absurd themes to catch layout issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plan for the exception.&lt;/strong&gt; Every client will want "just one small custom feature." Build your architecture to handle this without forking the codebase.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;José Gonçalves is the Founder &amp;amp; CEO of &lt;a href="https://mindconsulting.com.br" rel="noopener noreferrer"&gt;Mind Group Technologies&lt;/a&gt;, a software development company in Sorocaba, Brazil. We specialize in white-label SaaS platforms, multi-tenant architectures, and custom software for startups and enterprises. If you're considering building a white-label product, we'd love to share our experience — reach out at mindconsulting.com.br.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>saas</category>
      <category>webdev</category>
      <category>programming</category>
      <category>startup</category>
    </item>
    <item>
      <title>AI in Brazilian Healthcare: How Small Tech Teams Are Solving Problems Big Pharma Can't</title>
      <dc:creator>José Gonçalves</dc:creator>
      <pubDate>Fri, 13 Feb 2026 18:03:56 +0000</pubDate>
      <link>https://dev.to/jos_gonalves_fac39f3437/ai-in-brazilian-healthcare-how-small-tech-teams-are-solving-problems-big-pharma-cant-po1</link>
      <guid>https://dev.to/jos_gonalves_fac39f3437/ai-in-brazilian-healthcare-how-small-tech-teams-are-solving-problems-big-pharma-cant-po1</guid>
      <description>&lt;p&gt;Brazil's public healthcare system, the SUS (Sistema Único de Saúde), serves &lt;strong&gt;190+ million people&lt;/strong&gt;. It's one of the largest universal healthcare systems in the world. And it's struggling.&lt;/p&gt;

&lt;p&gt;Not because of lack of funding (though that's an issue), but because the technology infrastructure hasn't kept pace with the population's needs. There are only &lt;strong&gt;2.3 doctors per 1,000 people&lt;/strong&gt; in Brazil. In rural areas, that number drops dramatically.&lt;/p&gt;

&lt;p&gt;This is where small, focused tech teams are making an outsized impact. And I've had a front-row seat to this transformation.&lt;/p&gt;

&lt;p&gt;At &lt;strong&gt;&lt;a href="https://mindconsulting.com.br" rel="noopener noreferrer"&gt;Mind Group Technologies&lt;/a&gt;&lt;/strong&gt;, we've built healthcare platforms that serve clinics, hospitals, and telemedicine providers across Brazil. What we've learned is that the most impactful healthcare AI solutions aren't coming from billion-dollar pharma companies — they're coming from lean engineering teams that understand the local context.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Telemedicine Explosion
&lt;/h2&gt;

&lt;p&gt;COVID-19 did something remarkable for Brazilian healthcare: it forced the adoption of telemedicine almost overnight. Telemedicine consultations grew by over &lt;strong&gt;300%&lt;/strong&gt; during and after the pandemic.&lt;/p&gt;

&lt;p&gt;But here's what most people don't realize: the infrastructure behind those consultations was largely built by small tech companies. Not global giants.&lt;/p&gt;

&lt;p&gt;Why? Because Brazilian healthcare has unique requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LGPD compliance&lt;/strong&gt; (Brazil's data protection law, similar to GDPR)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration with SUS databases&lt;/strong&gt; and TISS/TUSS standards&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Portuguese-language NLP&lt;/strong&gt; for clinical documentation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low-bandwidth optimization&lt;/strong&gt; for rural connectivity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-device support&lt;/strong&gt; for varying hardware in public clinics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Global solutions don't address these requirements out of the box. Local teams do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where AI Is Actually Making a Difference
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Clinical Decision Support
&lt;/h3&gt;

&lt;p&gt;One of the most impactful projects we've worked on at &lt;strong&gt;Mind Group Technologies&lt;/strong&gt; involved building a clinical decision support system for a network of primary care clinics.&lt;/p&gt;

&lt;p&gt;The system analyzes patient symptoms, medical history, and lab results to suggest potential diagnoses and flag critical indicators that might be missed during a busy consultation.&lt;/p&gt;

&lt;p&gt;The key insight: we didn't try to replace doctors. We built a tool that gives overworked physicians a &lt;strong&gt;second opinion in real-time&lt;/strong&gt;. In a system where doctors see 30-40 patients per day, catching a missed diagnosis can literally save lives.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Medical Image Analysis
&lt;/h3&gt;

&lt;p&gt;Brazilian hospitals generate enormous volumes of medical images — X-rays, CT scans, MRIs. But radiologist availability is severely limited outside major urban centers.&lt;/p&gt;

&lt;p&gt;AI-powered image analysis doesn't replace radiologists. It &lt;strong&gt;prioritizes the queue&lt;/strong&gt;. When an AI model flags a chest X-ray as potentially showing pneumonia, that image gets reviewed first. In a hospital where a radiologist might be reviewing 200+ images per day, this prioritization matters.&lt;/p&gt;

&lt;p&gt;We built an integration layer at Mind Group Technologies that connects imaging AI models with existing PACS (Picture Archiving and Communication System) infrastructure in Brazilian hospitals. The technical challenge wasn't the AI model — it was making it work with legacy DICOM systems and ensuring LGPD-compliant data handling.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Patient Flow Optimization
&lt;/h3&gt;

&lt;p&gt;Emergency departments in Brazilian public hospitals are notoriously overwhelmed. AI-driven patient flow systems can predict admission rates, optimize bed allocation, and reduce wait times.&lt;/p&gt;

&lt;p&gt;The data exists — most hospitals track patient flow digitally. What's missing is the intelligence layer that turns this data into actionable predictions.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Drug Interaction Checking
&lt;/h3&gt;

&lt;p&gt;With an aging population taking multiple medications, drug interaction checking is critical. AI systems can cross-reference patient medication lists against interaction databases in real-time, flagging potential issues before prescriptions are filled.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Stack for Healthcare AI in Brazil
&lt;/h2&gt;

&lt;p&gt;For developers interested in this space, here's what we've found works at Mind Group Technologies:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend:&lt;/strong&gt; Python (FastAPI) for AI/ML services, Node.js for application logic&lt;br&gt;
&lt;strong&gt;AI/ML:&lt;/strong&gt; PyTorch for custom models, Hugging Face for NLP tasks, ONNX for model deployment&lt;br&gt;
&lt;strong&gt;Database:&lt;/strong&gt; PostgreSQL with proper encryption at rest, Redis for caching&lt;br&gt;
&lt;strong&gt;Compliance:&lt;/strong&gt; LGPD consent management, audit logging, data anonymization pipelines&lt;br&gt;
&lt;strong&gt;Infrastructure:&lt;/strong&gt; AWS (São Paulo region) or Azure for LGPD data residency requirements&lt;br&gt;
&lt;strong&gt;Integration:&lt;/strong&gt; HL7 FHIR for interoperability, TISS/TUSS for insurance standards&lt;/p&gt;

&lt;p&gt;The most important technical decision? &lt;strong&gt;Data residency&lt;/strong&gt;. Brazilian healthcare data must stay in Brazil. This eliminates many global SaaS solutions and creates opportunities for local developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges We've Encountered
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Data quality is the biggest bottleneck.&lt;/strong&gt; Brazilian healthcare data is messy. Inconsistent coding, missing fields, handwritten records that need OCR. We spend more time on data cleaning than model training.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regulatory uncertainty.&lt;/strong&gt; ANVISA (Brazil's health regulatory agency) is still developing guidelines for AI in healthcare. Building systems that are flexible enough to adapt to evolving regulations is essential.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Digital literacy varies enormously.&lt;/strong&gt; A surgeon in São Paulo and a community health worker in Amazonas have very different technology comfort levels. UX must account for this range.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connectivity is not guaranteed.&lt;/strong&gt; Many healthcare facilities in Brazil have unreliable internet. AI solutions need offline capabilities or graceful degradation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Opportunity
&lt;/h2&gt;

&lt;p&gt;Brazil's healthcare AI market is growing rapidly. The convergence of telemedicine adoption, government digitization initiatives, increasing smartphone penetration, and growing health tech investment creates a unique window.&lt;/p&gt;

&lt;p&gt;For developers, the opportunity is clear: &lt;strong&gt;build solutions that work within Brazilian healthcare's specific constraints, not despite them&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The teams that understand LGPD, SUS integration, Portuguese NLP, and the realities of healthcare delivery in a country of continental dimensions will build the solutions that matter most.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Involved
&lt;/h2&gt;

&lt;p&gt;If you're a developer interested in healthcare AI in Brazil:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Learn LGPD&lt;/strong&gt; — Data protection is non-negotiable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Study HL7 FHIR&lt;/strong&gt; — Healthcare interoperability standard&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understand SUS&lt;/strong&gt; — The public system serves most of the population&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build for low bandwidth&lt;/strong&gt; — Not everyone has fiber internet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partner with clinicians&lt;/strong&gt; — The best tech is built alongside medical professionals&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;José Gonçalves is the Founder &amp;amp; CEO of &lt;a href="https://mindconsulting.com.br" rel="noopener noreferrer"&gt;Mind Group Technologies&lt;/a&gt;, a software development company in Sorocaba, Brazil. We build healthcare platforms, AI solutions, and custom software for the Brazilian market. If you're working on health tech and need a technical partner with deep experience in LGPD, SUS integration, and local healthcare infrastructure, reach out at mindconsulting.com.br.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>webdev</category>
      <category>python</category>
    </item>
    <item>
      <title>Why 73% of Software Projects Fail — And What the Top 27% Do Differently</title>
      <dc:creator>José Gonçalves</dc:creator>
      <pubDate>Fri, 13 Feb 2026 17:59:19 +0000</pubDate>
      <link>https://dev.to/jos_gonalves_fac39f3437/why-73-of-software-projects-fail-and-what-the-top-27-do-differently-2ek1</link>
      <guid>https://dev.to/jos_gonalves_fac39f3437/why-73-of-software-projects-fail-and-what-the-top-27-do-differently-2ek1</guid>
      <description>&lt;p&gt;The number hit me during a coffee meeting in São Paulo. A CTO from one of Brazil's fastest-growing logistics companies was telling me about their latest failed software initiative. "Another $2 million down the drain," he said. "And we're not alone."&lt;/p&gt;

&lt;p&gt;He was right. According to the Standish Group CHAOS Report, approximately &lt;strong&gt;73% of IT projects&lt;/strong&gt; globally either fail outright or face significant challenges. In Brazil, where I've founded and run &lt;strong&gt;&lt;a href="https://mindconsulting.com.br" rel="noopener noreferrer"&gt;Mind Group Technologies&lt;/a&gt;&lt;/strong&gt; since 2016, I've watched this statistic play out across dozens of projects — some we've inherited mid-disaster, others we've guided to success from day one.&lt;/p&gt;

&lt;p&gt;But here's what fascinates me: the remaining 27%? They're not using secret technology. They're not spending three times the budget. They're doing something fundamentally different in how they approach software development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Software Projects Fail at Alarming Rates
&lt;/h2&gt;

&lt;p&gt;Brazil hosts the largest IT market in Latin America, worth over &lt;strong&gt;$50 billion annually&lt;/strong&gt;. We have more than &lt;strong&gt;500,000 software developers&lt;/strong&gt;. The raw talent is undeniable. Yet we fail at disproportionate rates. Why?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Misaligned expectations between stakeholders and developers.&lt;/strong&gt; A fintech client once told me their team believed they were building a real-time payment system, while the business actually wanted a standard processing pipeline. Eight months in, nobody had caught this disconnect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Poor requirement definition.&lt;/strong&gt; We joined a healthcare technology project after six months of development. The developers had built something technically impressive that nobody actually needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lack of continuous stakeholder engagement.&lt;/strong&gt; Teams work in isolation for months, then present a massive deliverable that misses the mark.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Technology chosen before problems are understood.&lt;/strong&gt; I've seen organizations fall in love with a specific tech stack and then contort their problems to fit it.&lt;/p&gt;

&lt;p&gt;According to ABSOFT, the average software project in Brazil runs &lt;strong&gt;40% over budget&lt;/strong&gt; and &lt;strong&gt;35% over schedule&lt;/strong&gt;. For a $1 million project, that's an extra $400,000 and several months of delay.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Successful 27% Actually Do
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Define Requirements with Painful Clarity
&lt;/h3&gt;

&lt;p&gt;The best projects we've worked on at &lt;strong&gt;Mind Group Technologies&lt;/strong&gt; start with requirements that are almost uncomfortably detailed. Not because we like bureaucracy, but because clarity is the cheapest investment you'll ever make.&lt;/p&gt;

&lt;p&gt;One of our e-commerce clients spent three weeks mapping user journeys before a single line of code was written. The result? Their MVP shipped with &lt;strong&gt;89% fewer change requests&lt;/strong&gt; than their previous project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; Spend 15-20% of project time on requirements. It feels slow. It saves you 50% of actual development time.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Establish Real-Time Feedback Loops
&lt;/h3&gt;

&lt;p&gt;The waterfall approach is still disturbingly common. Teams disappear for six months and emerge with something nobody wants.&lt;/p&gt;

&lt;p&gt;The successful projects we've partnered on use &lt;strong&gt;biweekly demos to actual users&lt;/strong&gt;. Not demos to stakeholders. To the people who'll use the software. At Mind Group Technologies, we ensure these demos are mandatory and drive concrete feedback.&lt;/p&gt;

&lt;p&gt;In a healthcare platform we built, weekly user feedback caught a critical UX flaw in week three. Fixing it then cost hours. At launch, it would have grounded the entire deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Use Clear Tech Stack Decisions as a Foundation
&lt;/h3&gt;

&lt;p&gt;Too many projects chase shiny frameworks. The most resilient projects make tech stack decisions based on: &lt;strong&gt;team expertise, project requirements, scalability needs, and maintainability&lt;/strong&gt;. In that order.&lt;/p&gt;

&lt;p&gt;A logistics client chose a pragmatic &lt;code&gt;Node.js + PostgreSQL + React&lt;/code&gt; stack. Not because it's trendy, but because the team knew it deeply. That project shipped and has run smoothly for three years.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Implement Risk Management (Before Disaster)
&lt;/h3&gt;

&lt;p&gt;Most failing projects have risk management documents. Nobody reads them.&lt;/p&gt;

&lt;p&gt;At &lt;strong&gt;Mind Group Technologies&lt;/strong&gt;, we treat risk management as a living practice — a lightweight risk register reviewed at every sprint planning meeting.&lt;/p&gt;

&lt;p&gt;For a fintech project, we identified "regulatory change" as a critical risk in week one. When regulations shifted, pivoting took &lt;strong&gt;two days instead of two months&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Measure What Actually Matters
&lt;/h3&gt;

&lt;p&gt;I've been in meetings where teams celebrate finishing "on time" while the product generates zero revenue.&lt;/p&gt;

&lt;p&gt;Successful projects track: &lt;strong&gt;user adoption rate, time to value, defect escape rate, and cost per feature delivered&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A health-tech company we partnered with tracked time-to-value obsessively. Their first module took 8 weeks. They got subsequent modules down to 3 weeks.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Build Change Management Into the Plan
&lt;/h3&gt;

&lt;p&gt;Most projects treat change requests as failures. Excellent projects treat them as &lt;strong&gt;learning signals&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At Mind Group Technologies, we reserve &lt;strong&gt;20% of capacity&lt;/strong&gt; for discoveries and changes. This isn't waste; it's planned learning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Examples
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Logistics Platform (2019):&lt;/strong&gt; A São Paulo company wanted to digitize their operation. Already six months late when Mind Group Technologies joined. We restarted with ruthless clarity, biweekly demos to warehouse managers, and a pragmatic tech stack. Result: shipped in 14 weeks, 40% under budget.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Fintech Integration:&lt;/strong&gt; A growing fintech wanted to integrate with five payment processors simultaneously. Instead of building a complex abstraction layer upfront, we recommended building for the first processor with a plugin architecture. Subsequent integrations were faster and more robust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Healthcare Platform:&lt;/strong&gt; A provider needed software to manage patient interactions. The first stakeholder demo revealed nobody had agreed on what "submission" meant. Early feedback, early course correction.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hard Truth
&lt;/h2&gt;

&lt;p&gt;Brazil's software development industry is world-class on talent and creativity. Our failure rate isn't because we lack smart people. It's because &lt;strong&gt;discipline isn't always valued as much as velocity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The successful 27% have learned that clarity upfront is faster than chaos in hindsight.&lt;/p&gt;

&lt;p&gt;If you're leading a software project — start with these: invest in requirement clarity, show real work to real users biweekly, let requirements drive technology, review risks openly, measure what matters, and expect learning.&lt;/p&gt;

&lt;p&gt;None of these require revolutionary tools. They require &lt;strong&gt;discipline and commitment&lt;/strong&gt; to shipping software that solves real problems.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;José Gonçalves is the Founder &amp;amp; CEO of &lt;a href="https://mindconsulting.com.br" rel="noopener noreferrer"&gt;Mind Group Technologies&lt;/a&gt;, a software development firm in Sorocaba, Brazil. Since 2016, Mind Group has delivered 100+ projects across logistics, fintech, healthcare, e-commerce, and education. Need a technical partner that prioritizes delivery discipline? Reach out at mindconsulting.com.br.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>startup</category>
      <category>career</category>
    </item>
    <item>
      <title>Brazil's Pix Processed $1.7 Trillion in 2023 — What That Means for Fintech Developers</title>
      <dc:creator>José Gonçalves</dc:creator>
      <pubDate>Fri, 13 Feb 2026 17:54:53 +0000</pubDate>
      <link>https://dev.to/jos_gonalves_fac39f3437/brazils-pix-processed-17-trillion-in-2023-what-that-means-for-fintech-developers-7pj</link>
      <guid>https://dev.to/jos_gonalves_fac39f3437/brazils-pix-processed-17-trillion-in-2023-what-that-means-for-fintech-developers-7pj</guid>
      <description>&lt;p&gt;If you're a developer building payment systems and you haven't studied Brazil's Pix, you're missing one of the most important case studies in modern fintech.&lt;/p&gt;

&lt;p&gt;In 2023, Pix — Brazil's instant payment system launched by the Central Bank in November 2020 — processed over &lt;strong&gt;R$17.2 trillion&lt;/strong&gt; (approximately $3.4 trillion USD) in transactions. To put that in perspective, that's more than the GDP of most countries, flowing through a system that didn't exist four years ago.&lt;/p&gt;

&lt;p&gt;As someone who's built fintech integrations at &lt;strong&gt;&lt;a href="https://mindconsulting.com.br" rel="noopener noreferrer"&gt;Mind Group Technologies&lt;/a&gt;&lt;/strong&gt; for Brazilian banks, payment processors, and financial startups, I want to break down what makes Pix technically fascinating — and what it means for developers worldwide.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scale Is Staggering
&lt;/h2&gt;

&lt;p&gt;Let's start with the numbers that matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;150+ million individual users&lt;/strong&gt; (in a country of ~215 million)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;14.7 million business accounts&lt;/strong&gt; actively using Pix&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3.5+ billion transactions per month&lt;/strong&gt; at peak&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Average processing time: 1.5 seconds&lt;/strong&gt; end-to-end&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Available 24/7/365&lt;/strong&gt; — no business hours, no holidays&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero cost for individuals&lt;/strong&gt; — completely free to send/receive&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For developers, that last point is revolutionary. Pix eliminated the primary revenue model of traditional payment processors and forced the entire ecosystem to innovate on top of the infrastructure rather than compete with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Pix Actually Works (The Developer's Perspective)
&lt;/h2&gt;

&lt;p&gt;At its core, Pix is a real-time gross settlement system (RTGS) operated by Brazil's Central Bank (BCB). But what makes it different from other instant payment systems globally?&lt;/p&gt;

&lt;h3&gt;
  
  
  The Key Architecture
&lt;/h3&gt;

&lt;p&gt;Pix uses a &lt;strong&gt;centralized clearing model&lt;/strong&gt; where the BCB acts as the single settlement authority. Every Pix transaction follows this flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Initiation&lt;/strong&gt; → User triggers payment via QR code, key (CPF, email, phone, random key), or manual entry&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt; → Participating institution validates the payer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorization&lt;/strong&gt; → Payer confirms the transaction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Settlement&lt;/strong&gt; → BCB processes the transaction through SPI (Sistema de Pagamentos Instantâneos)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notification&lt;/strong&gt; → Both parties receive confirmation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The entire cycle completes in under 10 seconds, with most transactions settling in 1-2 seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  The API Layer
&lt;/h3&gt;

&lt;p&gt;For developers integrating Pix, the Central Bank provides a standardized API specification that all &lt;strong&gt;800+ participating financial institutions&lt;/strong&gt; must implement. This includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pix Cob (Cobrança)&lt;/strong&gt; — For generating payment requests with QR codes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pix Webhook&lt;/strong&gt; — Real-time notifications for incoming/outgoing payments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pix Devolução&lt;/strong&gt; — Refund/return mechanisms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pix Dict&lt;/strong&gt; — The centralized key directory (DICT - Diretório de Identificadores de Contas Transacionais)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At &lt;strong&gt;Mind Group Technologies&lt;/strong&gt;, we've integrated Pix APIs for multiple clients across different banking partners. One of the most interesting technical challenges is handling the &lt;strong&gt;eventual consistency&lt;/strong&gt; model — where the SPI confirms settlement but individual banks may have slight delays in reflecting balances.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Developers Can Learn From Pix
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Standardization Beats Innovation (Sometimes)
&lt;/h3&gt;

&lt;p&gt;The BCB didn't try to build the most innovative payment system. They built the most &lt;strong&gt;standardized&lt;/strong&gt; one. Every bank implements the same API spec. Every QR code follows the same format (EMVCo standard). Every key lookup uses the same DICT service.&lt;/p&gt;

&lt;p&gt;This standardization means a developer can build one Pix integration and it works with every bank in Brazil. Compare this to the US, where integrating with each payment processor requires separate implementations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway for developers:&lt;/strong&gt; Sometimes the most impactful technical decision is choosing standards over proprietary solutions.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Open Banking Amplifies Everything
&lt;/h3&gt;

&lt;p&gt;Brazil launched its &lt;strong&gt;Open Finance&lt;/strong&gt; initiative alongside Pix, with 800+ institutions sharing data through standardized APIs. This combination created an ecosystem where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fintech startups can access bank data with user consent&lt;/li&gt;
&lt;li&gt;Payment initiation can happen from any authorized app&lt;/li&gt;
&lt;li&gt;Credit scoring can use real transaction history&lt;/li&gt;
&lt;li&gt;Account aggregation is a first-class feature&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We've built dashboards at &lt;strong&gt;Mind Group Technologies&lt;/strong&gt; that aggregate Pix transaction data across multiple bank accounts for business clients. The Open Finance APIs make this possible without screen scraping or manual CSV imports.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Real-Time Systems Require Real-Time Thinking
&lt;/h3&gt;

&lt;p&gt;Building on top of Pix taught our team at Mind Group Technologies to think differently about system design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Webhook reliability is critical&lt;/strong&gt; — You can't poll for Pix notifications. Missed webhooks mean missed payments. We implement retry queues, dead letter handling, and reconciliation jobs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency is non-negotiable&lt;/strong&gt; — With transactions settling in seconds, duplicate detection must be bullet-proof. Every Pix transaction has a unique &lt;code&gt;endToEndId&lt;/code&gt; that must be tracked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring must be real-time&lt;/strong&gt; — When your payment system processes transactions 24/7, your monitoring needs to match. We use real-time alerting for failed webhooks, settlement delays, and reconciliation mismatches.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Nubank Effect
&lt;/h2&gt;

&lt;p&gt;No discussion of Brazilian fintech is complete without mentioning &lt;strong&gt;Nubank&lt;/strong&gt; — now the largest digital bank in the world with &lt;strong&gt;80+ million customers&lt;/strong&gt;. Nubank's success proved that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Brazilian consumers are ready for digital-first financial services&lt;/li&gt;
&lt;li&gt;The market can support massive scale&lt;/li&gt;
&lt;li&gt;Technology companies can compete with traditional banks&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nubank's engineering team has published excellent technical content about their architecture. Their use of Clojure, Datomic, and event-driven architecture has inspired many Brazilian fintech teams, including ours.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Coming Next
&lt;/h2&gt;

&lt;p&gt;The BCB isn't done. Upcoming Pix features that developers should watch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pix Automático&lt;/strong&gt; — Scheduled recurring payments (like direct debit but instant)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pix Internacional&lt;/strong&gt; — Cross-border instant payments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pix por Aproximação&lt;/strong&gt; — NFC-based Pix payments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart Contracts on Pix&lt;/strong&gt; — Programmable payments with conditions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these features will create new integration opportunities and product possibilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  For International Developers
&lt;/h2&gt;

&lt;p&gt;If you're building fintech products and not studying Brazil's Pix, you're missing insights that will shape payment systems globally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;India's UPI&lt;/strong&gt; and &lt;strong&gt;Brazil's Pix&lt;/strong&gt; are the two most successful instant payment systems — both government-led, both free for consumers&lt;/li&gt;
&lt;li&gt;The EU's &lt;strong&gt;instant payments regulation&lt;/strong&gt; borrows concepts from both&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Fed's FedNow&lt;/strong&gt; in the US launched with similar ambitions but far less adoption&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The architectural patterns, API design decisions, and ecosystem dynamics of Pix provide a blueprint for what instant payments look like at nation-scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;If you want to experiment with Pix development:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sandbox environments&lt;/strong&gt; — Most major Brazilian banks (Itaú, Bradesco, Banco do Brasil) offer Pix sandbox APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BCB documentation&lt;/strong&gt; — The Central Bank publishes comprehensive API specs at bcb.gov.br&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open source libraries&lt;/strong&gt; — The Brazilian developer community has built excellent Pix libraries for Node.js, Python, and Java&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test with small amounts&lt;/strong&gt; — Pix has no minimum transaction value, so you can test with R$0.01&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;José Gonçalves is the Founder &amp;amp; CEO of &lt;a href="https://mindconsulting.com.br" rel="noopener noreferrer"&gt;Mind Group Technologies&lt;/a&gt;, a software house in Sorocaba, Brazil. We build fintech integrations, payment platforms, and custom software for startups and enterprises across Latin America. If you're looking for a technical partner experienced in Pix, Open Finance, and Brazilian fintech infrastructure, reach out at mindconsulting.com.br.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>fintech</category>
      <category>webdev</category>
      <category>programming</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>Why US Companies Are Quietly Moving Dev Teams from India to Brazil</title>
      <dc:creator>José Gonçalves</dc:creator>
      <pubDate>Fri, 13 Feb 2026 17:49:24 +0000</pubDate>
      <link>https://dev.to/jos_gonalves_fac39f3437/why-us-companies-are-quietly-moving-dev-teams-from-india-to-brazil-1h4o</link>
      <guid>https://dev.to/jos_gonalves_fac39f3437/why-us-companies-are-quietly-moving-dev-teams-from-india-to-brazil-1h4o</guid>
      <description>&lt;p&gt;Something interesting is happening in the global software outsourcing market, and most people haven't noticed yet.&lt;/p&gt;

&lt;p&gt;Over the past three years, a growing number of US-based companies — from Series A startups to Fortune 500 enterprises — have been quietly shifting their development teams from India to Brazil. Not because Indian developers aren't talented (they absolutely are), but because the math has changed.&lt;/p&gt;

&lt;p&gt;I've seen this shift firsthand. As the founder of &lt;strong&gt;Mind Group Technologies&lt;/strong&gt; (mindconsulting.com.br), a software development firm based in Sorocaba, Brazil, we've onboarded teams from US companies that previously worked exclusively with Indian outsourcing firms. The reasons they give are remarkably consistent.&lt;/p&gt;

&lt;p&gt;Let me walk you through the numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Timezone Factor Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;Here's a simple question: when it's 2 PM in New York, what time is it in Bangalore?&lt;/p&gt;

&lt;p&gt;It's 12:30 AM. Your developers are asleep.&lt;/p&gt;

&lt;p&gt;Now, what time is it in São Paulo? It's 4 PM. Your developers are in the same workday.&lt;/p&gt;

&lt;p&gt;This isn't a minor convenience — it's a fundamental shift in how distributed teams operate. Brazil sits 1-3 hours ahead of US Eastern Time, meaning real-time collaboration happens naturally. No 6 AM standups. No waiting 12 hours for a code review response.&lt;/p&gt;

&lt;p&gt;According to BRASSCOM (the Brazilian Association of Information Technology Companies), Brazil has over &lt;strong&gt;500,000 software developers&lt;/strong&gt; — one of the largest talent pools in the Western Hemisphere. And they're working in your timezone.&lt;/p&gt;

&lt;p&gt;At &lt;strong&gt;Mind Group Technologies&lt;/strong&gt;, we typically overlap 6-7 hours with US East Coast clients. That means pair programming, live debugging, and same-day PR reviews are the norm, not the exception.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Cost Comparison
&lt;/h2&gt;

&lt;p&gt;Let's talk money, because that's ultimately what drives these decisions.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;India (Annual)&lt;/th&gt;
&lt;th&gt;Brazil (Annual)&lt;/th&gt;
&lt;th&gt;US (Annual)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mid-Level Developer&lt;/td&gt;
&lt;td&gt;$24,000–$48,000&lt;/td&gt;
&lt;td&gt;$36,000–$72,000&lt;/td&gt;
&lt;td&gt;$120,000–$180,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Developer&lt;/td&gt;
&lt;td&gt;$48,000–$72,000&lt;/td&gt;
&lt;td&gt;$60,000–$96,000&lt;/td&gt;
&lt;td&gt;$150,000–$220,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tech Lead&lt;/td&gt;
&lt;td&gt;$60,000–$96,000&lt;/td&gt;
&lt;td&gt;$72,000–$108,000&lt;/td&gt;
&lt;td&gt;$180,000–$250,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Yes, Brazilian developers cost more than Indian developers. But here's what the spreadsheet doesn't show:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hidden costs of timezone misalignment:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;24-48 hour feedback loops on critical bugs&lt;/li&gt;
&lt;li&gt;Meetings at 6 AM or 10 PM (burnout, turnover)&lt;/li&gt;
&lt;li&gt;Miscommunication compounding over days instead of hours&lt;/li&gt;
&lt;li&gt;Sprint velocity loss of 20-30% due to async handoffs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you factor in these costs, the total cost of ownership often favors Brazil, especially for complex projects requiring tight collaboration.&lt;/p&gt;

&lt;h2&gt;
  
  
  English Proficiency: The Underrated Advantage
&lt;/h2&gt;

&lt;p&gt;Brazil ranks &lt;strong&gt;60th globally&lt;/strong&gt; on the EF English Proficiency Index — firmly in the "moderate" to "high" category and improving rapidly. More importantly, Brazilian developers in the tech sector tend to have significantly higher English proficiency than the national average, driven by exposure to English-language documentation, Stack Overflow, and international tech culture.&lt;/p&gt;

&lt;p&gt;India ranks higher on paper, but anyone who's managed distributed teams knows that fluency isn't just about test scores. It's about cultural communication patterns, directness in raising concerns, and comfort in challenging technical decisions.&lt;/p&gt;

&lt;p&gt;Brazilian developers tend to be direct communicators. When something won't work, they'll tell you. When a deadline is unrealistic, they'll push back. This saves projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cultural Alignment Factor
&lt;/h2&gt;

&lt;p&gt;This is harder to quantify but equally important.&lt;/p&gt;

&lt;p&gt;Brazilian work culture shares several traits with American work culture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Informal communication&lt;/strong&gt; — Brazilians are comfortable with casual Slack messages and impromptu calls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collaborative problem-solving&lt;/strong&gt; — less hierarchical than some Asian development cultures&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Startup mentality&lt;/strong&gt; — Brazil has the largest startup ecosystem in Latin America, with developers accustomed to fast iteration and uncertainty&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Western design sensibility&lt;/strong&gt; — UX/UI instincts aligned with US market expectations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At &lt;strong&gt;Mind Group Technologies&lt;/strong&gt;, we've found that our teams integrate with US clients almost seamlessly. There's rarely a "cultural adjustment period" — the working styles are naturally compatible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers Behind the Shift
&lt;/h2&gt;

&lt;p&gt;The data supports what we're seeing anecdotally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Latin America's IT outsourcing market is growing at &lt;strong&gt;20%+ annually&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Brazil's software export revenue reached &lt;strong&gt;$2.1 billion&lt;/strong&gt; in recent years&lt;/li&gt;
&lt;li&gt;The number of Brazilian developers on GitHub has grown &lt;strong&gt;40%+&lt;/strong&gt; in the past three years&lt;/li&gt;
&lt;li&gt;Major US companies like &lt;strong&gt;Google, Microsoft, and Nubank&lt;/strong&gt; have significant development operations in Brazil&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What This Means for Your Next Project
&lt;/h2&gt;

&lt;p&gt;If you're currently outsourcing to India and experiencing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frustrating timezone delays&lt;/li&gt;
&lt;li&gt;Communication gaps that slow down sprints&lt;/li&gt;
&lt;li&gt;Quality issues from async-heavy workflows&lt;/li&gt;
&lt;li&gt;Developer turnover disrupting project continuity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It might be worth exploring a Brazilian nearshore alternative.&lt;/p&gt;

&lt;p&gt;Here's what I'd recommend:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start with a pilot project&lt;/strong&gt; — Don't move everything at once. Test with a single module or feature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prioritize timezone overlap&lt;/strong&gt; — Look for firms in São Paulo or Southern Brazil for maximum US overlap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluate communication directly&lt;/strong&gt; — Schedule calls with actual developers, not just sales teams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check technical depth&lt;/strong&gt; — Brazil's developer community is strong in Node.js, React, Python, Java, and increasingly Rust and Go.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;The India-to-Brazil shift isn't about one country being "better" than another. It's about optimization. Different projects benefit from different models.&lt;/p&gt;

&lt;p&gt;For projects requiring tight collaboration, rapid iteration, and real-time communication with US teams, &lt;strong&gt;Brazil offers a compelling combination of talent, timezone, cost, and cultural alignment&lt;/strong&gt; that's hard to match.&lt;/p&gt;

&lt;p&gt;The companies making this shift aren't doing it loudly. They're doing it because it works.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;José Gonçalves is the Founder &amp;amp; CEO of &lt;a href="https://mindconsulting.com.br" rel="noopener noreferrer"&gt;Mind Group Technologies&lt;/a&gt;, a software development company based in Sorocaba, SP, Brazil. Since 2016, Mind Group has delivered 100+ projects across logistics, fintech, healthcare, e-commerce, and education. If you're exploring nearshore development options, reach out at mindconsulting.com.br.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>career</category>
      <category>startup</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
