<?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: Marcos Souza</title>
    <description>The latest articles on DEV Community by Marcos Souza (@marcossouzadotdev).</description>
    <link>https://dev.to/marcossouzadotdev</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F181512%2Fd8264d69-6ad9-41d8-b24b-36d05be16e18.png</url>
      <title>DEV Community: Marcos Souza</title>
      <link>https://dev.to/marcossouzadotdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marcossouzadotdev"/>
    <language>en</language>
    <item>
      <title>Contract First, Code Last: The Workflow That Stopped Us From Rebuilding Features Twice</title>
      <dc:creator>Marcos Souza</dc:creator>
      <pubDate>Thu, 16 Jul 2026 16:38:43 +0000</pubDate>
      <link>https://dev.to/marcossouzadotdev/contract-first-code-last-the-workflow-that-stopped-us-from-rebuilding-features-twice-4074</link>
      <guid>https://dev.to/marcossouzadotdev/contract-first-code-last-the-workflow-that-stopped-us-from-rebuilding-features-twice-4074</guid>
      <description>&lt;h2&gt;
  
  
  The request that looked like a one-line change
&lt;/h2&gt;

&lt;p&gt;A feature request came in that, on paper, sounded almost too simple to plan: &lt;em&gt;"Let therapists open a chart, click on an objective, and drill down into its targets and steps."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Technically, it was basically a one-liner. The data model already had everything — targets, steps, per-session scores, all wired up. The charting library was already in the codebase. The click target was one attribute away from being clickable. If I'd measured the task by "how many files change," I'd have said a day, maybe less.&lt;/p&gt;

&lt;p&gt;But "technically simple" and "correct on the first try" are unrelated properties of a feature, and this one was entirely UX-shaped. How do you lay out an unknown number of targets side by side without it collapsing into a wall of tiny charts? Does the section stay pinned while you scroll past it, or does that just eat the screen? What does a chart need to &lt;em&gt;say&lt;/em&gt; — not show, &lt;em&gt;say&lt;/em&gt; — for a therapist scanning five kids' worth of sessions to actually notice the thing that matters?&lt;/p&gt;

&lt;p&gt;None of that gets decided correctly on paper. It gets decided by looking at it, clicking it, and reacting honestly to what's in front of you — which is exactly the kind of feedback that's cheap to get on a static HTML file and expensive to get on a real component tree with real state, real i18n keys, and a real test suite attached to it.&lt;/p&gt;

&lt;p&gt;So that's what I did differently this time: I opened a pull request with an HTML file, not a feature. Here's the whole process, and — more importantly — what it actually bought back in product terms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Write the contract before opening an editor
&lt;/h2&gt;

&lt;p&gt;Before touching any code, I write down the shape of the problem in plain language — not "build the feature," but an actual contract: what triggers it, what data it needs, what "done" looks like, and, critically, what's still unknown.&lt;/p&gt;

&lt;p&gt;For this feature, the contract read roughly like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trigger:&lt;/strong&gt; clicking an objective's title on the existing graphs screen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output:&lt;/strong&gt; a modal — one area chart per target (absolute values, side by side), and below that, one card per target holding a stacked bar chart per step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interaction:&lt;/strong&gt; clicking a target's title scrolls the modal down to that target's step card.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit non-goal:&lt;/strong&gt; no schema change. The existing tables already modeled this correctly; the only gap was that the current query flattened everything down to the objective level instead of preserving target/step granularity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Writing the contract down &lt;em&gt;before&lt;/em&gt; opening an editor is what surfaces the gaps early instead of mid-implementation. Two real product questions fell out of this one immediately: how does the target row behave once there are more than two or three targets, and does that row stay visible while you scroll the step cards below it, or does the whole thing scroll together? Neither has a "correct" technical answer. They're product decisions, and the contract's job is to force them into the open where someone can actually decide them on purpose — instead of whoever writes the CSS three weeks later deciding by accident.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Match the tool to the phase, not the phase to the tool
&lt;/h2&gt;

&lt;p&gt;Working with an AI pair means the temptation is to reach for the same mode the whole way through — "have it write the feature." That's exactly backwards for anything UX-heavy. What actually worked was treating each phase as needing a different posture entirely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Exploration — facts only.&lt;/strong&gt; Read-only passes whose sole job was mapping what already exists: where's the click target, what charting library is already in use, what does the query layer look like today, is there prior art for a similar interaction (a modal, a stacked chart, a similar drill-down) anywhere else in the codebase. No opinions at this stage — just facts with file references, because every opinion formed before the facts are in is a guess wearing a confident voice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design — grounded in those facts.&lt;/strong&gt; Only after the exploration pass came back did I ask for a concrete implementation approach: which existing functions to reuse, which files actually need to change, and what the new query looks like. Grounded in what's real, not in a fresh pattern invented from scratch because nobody checked first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clarification — ask, don't guess.&lt;/strong&gt; The two open questions from the contract (target row layout, sticky-or-not) got asked directly instead of being assumed. A wrong guess here doesn't cost ten seconds — it costs a whole review cycle to notice and another to fix. Asking costs ten seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Craft — mockup, and only the mockup.&lt;/strong&gt; The last phase, and the one this whole post is really about, was building something you could actually click — grounded in the app's real design tokens (colors pulled straight out of the existing CSS custom properties, not invented), real content, and, as it turned out, a couple of rounds of "no, that's not it either."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The discipline that matters here isn't which tool you use in each phase — it's &lt;em&gt;not letting design opinions leak into the exploration phase&lt;/em&gt;, and not letting implementation start before the design phase has actually settled anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Build the mockup with real data, inside its real container
&lt;/h2&gt;

&lt;p&gt;This is the single highest-leverage decision in the whole process, and it's the one I'd underrated before doing it properly this time: &lt;strong&gt;the mockup used the product's actual seed data, not placeholder content.&lt;/strong&gt; Real objective titles pulled from the demo dataset. Real step-progression wording — actual ESDM prompt-fading levels ("with full physical support," "with partial physical support"), not generic "Step 1 / Step 2" labels. Real session dates. Real design tokens, lifted straight from the app's own CSS variables instead of somebody picking "a nice blue" that then has to be reconciled with the design system later.&lt;/p&gt;

&lt;p&gt;And it wasn't a chart floating alone on a blank page. It got dropped into a full reconstruction of the dashboard it actually lives in — same header, same surrounding sections competing for the same vertical space, same card chrome. A chart that looks great in isolation and a chart that looks great sitting under four other sections fighting for attention are two completely different design problems, and you only catch the second one if you're honest enough to build the container, not just the widget.&lt;/p&gt;

&lt;p&gt;I also wired in the real charting library — not hand-rolled SVG shapes standing in for it — specifically so "does this look right" was a meaningful question, evaluated against the same rendering engine that would actually ship. A mockup that fakes its own rendering engine just relocates the surprise to later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Iterate on the mockup — not on the pull request
&lt;/h2&gt;

&lt;p&gt;This is the part that actually pays the bill. The moment the mockup was clickable, two things surfaced that no amount of upfront contract-writing would have caught, because neither one is visible until you're looking at the real thing move:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The sticky header, which was the "obviously correct" choice on paper, was actively bad in practice.&lt;/strong&gt; Pinning the target-comparison row made complete sense in the abstract — keep the thing you're comparing against always in view while you scroll the detail below it. In practice, the moment real chart heights were in the picture (not the compressed placeholder heights from the first pass), that pinned section ate most of the modal's vertical space and left almost nothing for the content it was supposedly helping you compare against. Reverting it to a normal scroll took about two minutes once it was visible. It would have taken a design review, a follow-up ticket, and a second PR if it had shipped first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The invented step names read as exactly what they were — invented.&lt;/strong&gt; Swapping generic sub-skill labels for the real prompt-fading vocabulary from the seed data turned the "which step is actually driving the extra support load" callout from a UI feature into something that reads as clinically true. That's not a cosmetic difference — it's the difference between a stakeholder nodding along politely and a stakeholder saying "oh, &lt;em&gt;that's&lt;/em&gt; the kid I was thinking of, yeah, that's exactly the pattern."&lt;/p&gt;

&lt;p&gt;Neither of those is a defect you catch in code review. Code review checks whether the code does what the ticket says. It has no opinion on whether the ticket was right. You only find that out by looking at the thing move — and it is dramatically cheaper to find it out while "the thing" is a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; in a scratch file than while it's a component tree with hooks, query keys, and translation strings already wired through it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that actually matters: what this saves in a real product
&lt;/h2&gt;

&lt;p&gt;Here's the part I want to make concrete, because "validate before you build" sounds like a truism until you look at where the cost actually sits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rework after real implementation is not proportional to the size of the change.&lt;/strong&gt; A one-line CSS tweak in a static mockup is a one-line CSS tweak. The &lt;em&gt;same&lt;/em&gt; one-line tweak, discovered after the feature has shipped through real components, means: touching a component that has state and props flowing through it, possibly touching a hook, possibly touching a query key, definitely re-running the type checker, possibly touching translation strings in two locale files, definitely going through review again, and possibly re-testing a flow that had already been marked done. The size of the fix didn't change. The blast radius of &lt;em&gt;making&lt;/em&gt; the fix did.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every round of that avoided rework is a round of engineering time that goes somewhere else.&lt;/strong&gt; This is the part that's easy to lose sight of when you're heads-down on one feature: the hours not spent re-litigating a shipped component aren't just "saved," they're &lt;em&gt;redirected&lt;/em&gt; — onto the next item in the backlog, onto the bug that's been sitting for two sprints, onto the feature after this one. Across a quarter, the difference between "we validate before we build" and "we build, then fix what we got wrong" isn't a rounding error. It's the difference between shipping the roadmap and shipping most of the roadmap plus a trail of follow-up tickets for things that were "mostly right the first time."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A pull request that is honestly just a mockup gets more honest feedback, faster.&lt;/strong&gt; This sounds soft, but it's a real, measurable effect on review latency. When a reviewer knows a PR contains zero production code, they say "actually, I don't think this works" without the social friction of knowing that sentence costs someone a day of rewritten components. Bundling the UX conversation with the implementation makes disagreement expensive to raise, which quietly biases reviewers toward approving things they're not sure about. Splitting them apart makes disagreement cheap, which means you actually hear it — while it still costs ten minutes to act on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It compounds.&lt;/strong&gt; The second time you run this process on a similar feature, the contract-writing step is faster because you already know which questions tend to be the load-bearing ones. The mockup step is faster because half the design tokens and layout patterns are already proven out from the last one. None of this shows up as a single dramatic number — it shows up as the fourth feature in a related area, taking half the calendar time the first one did, for reasons nobody quite remembers to attribute back to "oh, right, we stopped rebuilding things."&lt;/p&gt;

&lt;h2&gt;
  
  
  The obvious order, and why it's the expensive one
&lt;/h2&gt;

&lt;p&gt;The default order is: write the code, put it behind a flag or a screenshot, get feedback, refactor. It's not wrong, exactly — it works, plenty of teams ship this way. But the feedback arrives &lt;em&gt;after&lt;/em&gt; the expensive part is already done. Every "actually, can the header not be sticky" becomes a diff against real components, real state, real tests, instead of a two-line edit in a file nobody's building on top of yet.&lt;/p&gt;

&lt;p&gt;Flip the order — mockup first, iterate cheaply, &lt;em&gt;then&lt;/em&gt; write the real thing — and the same disagreement, resolved in the same number of rounds, costs a fraction of what it used to. Same feature, same feedback, same number of "no, try agains. Just paid for at scratch-file prices instead of production-component prices.&lt;/p&gt;

&lt;p&gt;That's the whole trick. Not fewer mistakes — the same number of mistakes, caught somewhere cheaper.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>ai</category>
      <category>webdev</category>
      <category>ux</category>
    </item>
    <item>
      <title>Testes E2E como harness de AI: de 1h de QA manual a 10 min</title>
      <dc:creator>Marcos Souza</dc:creator>
      <pubDate>Sun, 05 Jul 2026 17:20:48 +0000</pubDate>
      <link>https://dev.to/marcossouzadotdev/testes-e2e-como-harness-de-ai-de-1h-de-qa-manual-a-10-min-4l1o</link>
      <guid>https://dev.to/marcossouzadotdev/testes-e2e-como-harness-de-ai-de-1h-de-qa-manual-a-10-min-4l1o</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; refizemos do zero a infra de testes E2E (Playwright + Docker + seeds determinísticos + contas por role) e ela virou o &lt;em&gt;harness&lt;/em&gt; que permite usar AI no desenvolvimento sem medo: 1h de UAT manual virou 5-10 min, QA roda sozinho no preview de cada PR, e bugs de permissão invisíveis vieram à tona. Aqui está o como e o porquê, passo a passo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Índice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;O problema: a AI acelerou tudo, menos o QA&lt;/li&gt;
&lt;li&gt;A tese: testes são o harness da AI&lt;/li&gt;
&lt;li&gt;A infra E2E: DB efêmero, seeds determinísticos e uma conta por role&lt;/li&gt;
&lt;li&gt;O pipeline de CI, do zero&lt;/li&gt;
&lt;li&gt;Fechando o loop: QA automatizado no preview do PR&lt;/li&gt;
&lt;li&gt;Resultados: o que isso vira em produto&lt;/li&gt;
&lt;li&gt;Takeaways&lt;/li&gt;
&lt;li&gt;Um obrigado — e por que resolvi escrever isto&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  O problema: a AI acelerou tudo, menos o QA
&lt;/h2&gt;

&lt;p&gt;Com AI no fluxo de desenvolvimento, passamos a produzir muito mais código — e isso expôs um gargalo clássico: &lt;strong&gt;review e QA não acompanharam a velocidade&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No nosso caso (uma plataforma SaaS multi-tenant, time pequeno), isso significava:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UATs manuais checando fluxo por fluxo, tirando print de tudo pra gerar evidência no PR&lt;/li&gt;
&lt;li&gt;DB local com estado "zoado" quebrando os testes E2E — e às vezes o próprio app&lt;/li&gt;
&lt;li&gt;Uma única conta de teste pra tudo, escondendo bugs de permissão&lt;/li&gt;
&lt;li&gt;PRs parados esperando alguém ter 1 hora livre pra validar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cada UAT manual custava ~1 hora. Hoje custa 5-10 minutos instruindo uma AI. Mas o ponto não é economizar tempo com o esporte — é o que você faz com o tempo de volta. Cada hora que não gasto clicando na tela manualmente é uma hora construindo produto: uma feature a mais, um fluxo mais bem pensado, uma dívida técnica paga. Esse post é sobre o que foi construído pra chegar aí — e por que isso é uma decisão de produto, não só de engenharia.&lt;/p&gt;

&lt;h2&gt;
  
  
  A tese: testes são o harness da AI
&lt;/h2&gt;

&lt;p&gt;"Harness", no contexto de coding com AI, é tudo aquilo que garante que a AI vai fazer a coisa certa: o ambiente, os guardrails, os loops de feedback. A gente enxerga os testes exatamente assim.&lt;/p&gt;

&lt;p&gt;O raciocínio central é este: se os testes unit cobrem os casos da lógica, os E2E cobrem todos os fluxos do usuário, e o código gerado passou no review seguindo os padrões do projeto — &lt;strong&gt;o que sobra pra você "vasculhar" manualmente?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sempre sobra algo — exceções, detalhes de UX. Mas o grosso da confiança vem do harness. E isso muda a relação com a AI: em vez de revisar cada linha com desconfiança, você deixa o harness fazer o trabalho pesado e foca sua atenção onde ela vale mais.&lt;/p&gt;

&lt;p&gt;Sem esse harness, o cenário se inverte: a AI gera código mais rápido do que um humano consegue revisar, e &lt;strong&gt;a velocidade vira passiva&lt;/strong&gt; — bugs entram na mesma velocidade que as features, regressões silenciosas passam despercebidas até o cliente reclamar, e cada mudança da AI exige UAT manual de novo, de volta à estaca zero.&lt;/p&gt;

&lt;p&gt;E aqui a coisa deixa de ser técnica e vira produto: um harness sólido é o que permite ao time &lt;strong&gt;se mover rápido sem medo&lt;/strong&gt;. Quando você confia que os fluxos críticos estão cobertos, aceita mudanças maiores, refatora sem pânico, entrega mais. Quando não confia, cada deploy é uma aposta — e o custo disso não aparece no board, aparece no churn, no suporte, na confiança do usuário que quebra silenciosamente. Qualidade não é o oposto de velocidade; é o que torna a velocidade sustentável.&lt;/p&gt;

&lt;h2&gt;
  
  
  A infra E2E: DB efêmero, seeds determinísticos e uma conta por role
&lt;/h2&gt;

&lt;p&gt;No final do ano passado, fiquei responsável por refazer a infra e os testes E2E do zero. Minha abordagem foi incremental: primeiro o básico funcionando, depois estudar os padrões, e refatorar aplicando boas práticas o quanto antes.&lt;/p&gt;

&lt;h3&gt;
  
  
  DB efêmero com Docker: o teste é dono do banco
&lt;/h3&gt;

&lt;p&gt;O maior problema era o estado do banco — a solução foi tirar esse estado da equação. Ao rodar &lt;code&gt;pnpm test:e2e:ui&lt;/code&gt;, o global setup do Playwright sobe um MySQL isolado, roda migrations e seeds, e tira um snapshot restaurado entre arquivos de teste:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// tests/e2e/global-setup.ts (simplificado)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&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;globalSetup&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;dbManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startContainer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// MySQL 8 dedicado via Docker (porta própria, DB isolado)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dbManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForMySql&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;    &lt;span class="c1"&gt;// espera o healthcheck&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dbManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runMigrations&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;dbManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runSeeds&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;        &lt;span class="c1"&gt;// demo-data + seed-data de teste&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dbManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSnapshot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// snapshot: restaurado entre arquivos de teste&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="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dbManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validateTestUser&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;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test user not found after seeding!&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;O resultado é sempre idêntico, local e no CI. Até o MySQL do Compose tem tuning pra teste (&lt;code&gt;innodb-flush-log-at-trx-commit=0&lt;/code&gt;, buffer pool ajustado): durabilidade não importa num banco descartável, velocidade sim. E como o banco é restaurado entre arquivos de teste, um spec não contamina o outro.&lt;/p&gt;

&lt;h3&gt;
  
  
  Uma conta por role: o detalhe que mais pagou
&lt;/h3&gt;

&lt;p&gt;Testar tudo com uma única conta admin escondia exatamente os bugs de permissão que mais importavam — porque admin passa em qualquer tela. A correção foi criar um provisionamento de contas cobrindo &lt;strong&gt;todas as roles do sistema&lt;/strong&gt;, definidas numa única fonte de verdade tipada:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// tests/e2e/setup/test-credentials.ts (trecho)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;E2EAccountKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;owner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;main-supervisor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;supervisor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;therapist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;therapist-assistant&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;relative&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;E2E_ACCOUNTS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;E2EAccountKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;E2EAccountDefinition&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;supervisor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;supervisor@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;memberRole&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MemberRoles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MEMBER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;staffProfession&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;StaffProfessionEnum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SUPERVISOR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;assignmentAttribution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;StaffAttributionEnum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SUPERVISOR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;// ...uma entrada por role, seedada no setup e logada via storageState&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assim que cada teste passou a usar a role certa, começaram a aparecer bugs de &lt;code&gt;access denied&lt;/code&gt; que ninguém estava percebendo — porque nenhum fluxo era exercitado com as permissões reais do usuário final. Se você tem RBAC e testa tudo com admin, seus testes de permissão não existem.&lt;/p&gt;

&lt;p&gt;Esse foi, disparado, o investimento com melhor retorno da infra toda — e não por acaso o mais ligado ao produto: bug de permissão não é bug técnico qualquer, é um usuário vendo dado que não devia, ou travado fora de algo que era pra ele. É exatamente o tipo de coisa que corrói confiança e é caríssima de descobrir em produção.&lt;/p&gt;

&lt;h3&gt;
  
  
  POM + BDD: o formato que a AI entende
&lt;/h3&gt;

&lt;p&gt;Os testes seguem Page Object Model, organizados por domínio (&lt;code&gt;pages/child&lt;/code&gt;, &lt;code&gt;pages/relatives&lt;/code&gt;, &lt;code&gt;pages/session&lt;/code&gt;...), com fixtures e test-data builders. E opcionalmente escrevemos &lt;code&gt;.feature&lt;/code&gt; files (Gherkin/BDD) descrevendo os fluxos — isso virou documentação executável, e é aqui que a AI brilha: com fluxos documentados, POMs consistentes e o Playwright MCP disponível, &lt;strong&gt;a AI gera specs E2E novos seguindo os padrões do projeto&lt;/strong&gt;, em vez de inventar seletores frágeis do zero. Antes dessa estrutura existir, gerar E2E com AI simplesmente não funcionava direito.&lt;/p&gt;

&lt;p&gt;Um exemplo real, de um fluxo crítico que envolve &lt;strong&gt;duas roles no mesmo teste&lt;/strong&gt;: o supervisor convida um responsável, e o responsável abre o convite &lt;em&gt;no próprio dispositivo&lt;/em&gt; e preenche a anamnese (ficha clínica de entrada) da criança:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;&lt;span class="kn"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; Invited relative completes every editable anamnese field
  &lt;span class="nf"&gt;Given &lt;/span&gt;a main supervisor is logged in
  &lt;span class="nf"&gt;And &lt;/span&gt;the supervisor invited and assigned a relative to the seeded child
  &lt;span class="nf"&gt;And &lt;/span&gt;the relative completed registration from the invitation on their device
  &lt;span class="nf"&gt;When &lt;/span&gt;the relative completes every editable anamnese field
  &lt;span class="nf"&gt;Then &lt;/span&gt;the draft is autosaved and remains complete after reload
  &lt;span class="nf"&gt;When &lt;/span&gt;the relative submits the anamnese
  &lt;span class="nf"&gt;Then &lt;/span&gt;the submitted answers remain complete for the relative
  &lt;span class="nf"&gt;And &lt;/span&gt;the main supervisor sees the same complete anamnese
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E o spec segue o cenário passo a passo — cada &lt;code&gt;test.step&lt;/code&gt; espelha uma linha do Gherkin, então o report do Playwright lê como o fluxo de negócio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// testIsolated: restaura o snapshot do DB antes de cada teste (inclusive retries)&lt;/span&gt;
&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;restoreDb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;storageState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;playwright/.auth/main-supervisor.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// role certa, já logada&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invited relative completes every editable anamnese field&lt;/span&gt;&lt;span class="dl"&gt;'&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;browser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&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="c1"&gt;// Contexto incógnito separado = o relative no "próprio dispositivo",&lt;/span&gt;
  &lt;span class="c1"&gt;// sem derrubar a sessão do supervisor&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;relativeContext&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newContext&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;storageState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="na"&gt;origins&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="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;relativePage&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;relativeContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&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;registration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InviteRegistrationPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;relativePage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// POMs por domínio&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;relativeAnamnese&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AnamnesePage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;relativePage&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;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Given a main supervisor invited and assigned a relative to the seeded child&lt;/span&gt;&lt;span class="dl"&gt;'&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="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;relatives&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createRelative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&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;childId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;relativeEmail&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;relativeName&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;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;And the relative completed registration from the invitation on their device&lt;/span&gt;&lt;span class="dl"&gt;'&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="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;invitation&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="nx"&gt;invitation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findFirstOrThrow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;relativeEmail&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;registration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;invitationId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;invitation&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="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;relativeEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// ...When/Then: preenche todos os campos, valida autosave após reload,&lt;/span&gt;
  &lt;span class="c1"&gt;// submete, e confere que o SUPERVISOR vê a mesma anamnese completa&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repare nos detalhes que o harness carrega: a role entra pronta via &lt;code&gt;storageState&lt;/code&gt;, o segundo login vive num browser context isolado (multiusuário real, sem gambiarras de logout), e o &lt;code&gt;testIsolated&lt;/code&gt; restaura o snapshot do banco antes de cada teste — então esse fluxo inteiro de convite + registro + formulário roda quantas vezes for preciso, sempre a partir do mesmo estado.&lt;/p&gt;

&lt;h2&gt;
  
  
  O pipeline de CI, do zero
&lt;/h2&gt;

&lt;p&gt;A regra é simples: barato e rápido roda primeiro, caro roda só se o resto já passou. Quando um PR sai de draft, o workflow roda em dois jobs encadeados:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job 1 — Quality Checks (gate rápido, ~15 min de timeout):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Quality checks (lint, typecheck, etc.)&lt;/li&gt;
&lt;li&gt;Testes unit&lt;/li&gt;
&lt;li&gt;Testes de integração&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Job 2 — E2E (só roda se o Job 1 passar):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sobe MySQL 8 via Docker num DB isolado (&lt;code&gt;proaba_e2e_test&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Gera um &lt;code&gt;.env&lt;/code&gt; de teste (secrets fake, &lt;code&gt;NODE_ENV=test&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Instala o Chromium do Playwright&lt;/li&gt;
&lt;li&gt;Roda o global setup (healthcheck → migrations → seeds → snapshot → validação)&lt;/li&gt;
&lt;li&gt;Executa a suíte E2E completa&lt;/li&gt;
&lt;li&gt;Faz upload do report do Playwright como artifact (mesmo em falha)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Como o banco nasce e morre dentro do job, &lt;strong&gt;zero dependência de ambiente compartilhado&lt;/strong&gt; — o clássico "passa local, quebra no CI" praticamente sumiu.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fechando o loop: QA automatizado no preview do PR
&lt;/h2&gt;

&lt;p&gt;Com a base pronta, o core dev do projeto montou em cima um fluxo de &lt;strong&gt;pr-qa&lt;/strong&gt;: cada PR gera um preview deployment, e quando a URL fica disponível, um agente valida o fluxo de ponta a ponta sozinho — e entrega um veredito de merge baseado em evidência, não em achismo.&lt;/p&gt;

&lt;p&gt;Na prática, o agente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lê o diff do PR e mapeia o "blast radius" — quais telas/fluxos renderizam o código alterado&lt;/li&gt;
&lt;li&gt;Loga no preview e navega os fluxos afetados &lt;strong&gt;em todos os locales&lt;/strong&gt; (pt-br e en)&lt;/li&gt;
&lt;li&gt;Para toda mutação no diff (form, POST/PATCH/DELETE): &lt;strong&gt;preenche, submete, valida o toast de sucesso, recarrega e confirma que persistiu&lt;/strong&gt; — e depois reverte. Uma tela que renderiza mas não salva é bug silencioso de perda de dados&lt;/li&gt;
&lt;li&gt;Roda um analyzer estático de regressão de i18n (chaves removidas mas ainda usadas) + scan do DOM procurando raw keys&lt;/li&gt;
&lt;li&gt;Captura screenshots de tudo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;E o PR template fecha o ciclo do outro lado: ele tem uma persona embutida pra AI que escreve a descrição, com &lt;strong&gt;honesty rules&lt;/strong&gt; explícitas — não pode afirmar que testes/UAT passaram sem incluir output, link de CI ou screenshot; se não consegue verificar, deixa um &lt;code&gt;TODO(HUMAN)&lt;/code&gt;. Um check no CI valida que o template foi preenchido. Ou seja: até o "a AI disse que funciona" precisa de evidência.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resultados: o que isso vira em produto
&lt;/h2&gt;

&lt;p&gt;Em números diretos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1 hora de UAT manual → 5-10 minutos&lt;/strong&gt; instruindo a AI, que roda os fluxos e captura evidências sozinha&lt;/li&gt;
&lt;li&gt;Ciclo PR aberto → review → merge muito mais rápido; muitas horas economizadas por semana&lt;/li&gt;
&lt;li&gt;Bugs de permissão que estavam invisíveis apareceram assim que as roles certas entraram nos testes&lt;/li&gt;
&lt;li&gt;Um efeito composto: quanto mais testes entram, maior a chance de pegar o próximo bug antes do usuário&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mas os números só importam pelo que destravam. As horas que voltam não somem numa planilha de produtividade — elas viram capacidade de construir: mais features entregues, mais tempo pra ouvir o usuário e pensar em UX, menos gente parada apagando incêndio de regressão. A qualidade que o harness garante é o que deixa a equipe &lt;strong&gt;entregar rápido, continuando a confiar no que entrega&lt;/strong&gt;. Num time pequeno, essa é a diferença entre estar sempre correndo atrás e conseguir de fato empurrar o produto pra frente. No fim, testar bem é uma alavanca de produto disfarçada de disciplina de engenharia.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Testes são o harness da AI.&lt;/strong&gt; Sem eles, a velocidade da AI vira passivo; com eles, vira velocidade de verdade.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;O teste tem que ser dono do banco.&lt;/strong&gt; DB efêmero + seeds determinísticos + snapshot mata a maior fonte de flakiness.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teste com as roles reais.&lt;/strong&gt; Uma conta admin pra tudo esconde exatamente os bugs mais graves.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Estrutura primeiro, AI depois.&lt;/strong&gt; POM, fixtures e fluxos documentados são o que fazem a geração de teste por AI funcionar — não o contrário.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evidência &amp;gt; afirmação.&lt;/strong&gt; QA automatizado com screenshots e mutation testing, e PR descriptions que não podem alegar sucesso sem prova.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Um obrigado — e por que resolvi escrever isto
&lt;/h2&gt;

&lt;p&gt;Confesso que escrever sobre o próprio trabalho não é natural pra mim. O empurrão veio de um livro pequeno do &lt;strong&gt;Austin Kleon&lt;/strong&gt;, o &lt;em&gt;&lt;a href="https://austinkleon.com/show-your-work/" rel="noopener noreferrer"&gt;Show Your Work!&lt;/a&gt;&lt;/em&gt;. A ideia que me pegou foi a de compartilhar o &lt;strong&gt;processo&lt;/strong&gt;, não só o resultado polido — e a de que ensinar o que você aprendeu não subtrai valor do seu trabalho, pelo contrário: agrega. Foi exatamente com esse espírito que documentei aqui não o "temos testes E2E", mas o &lt;em&gt;como&lt;/em&gt; e o &lt;em&gt;porquê&lt;/em&gt; de cada decisão.&lt;/p&gt;

&lt;p&gt;Então, se algo aqui te poupar algumas das horas que eu gastei descobrindo na marra — sobre banco efêmero, contas por role ou usar testes como harness de AI — já valeu. Obrigado, Kleon, pelo empurrão. E se você tem um harness melhor, ou discorda de alguma escolha, me conta nos comentários: sigo aprendendo em público. 🙂&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>portuguese</category>
      <category>ai</category>
      <category>playwright</category>
    </item>
    <item>
      <title>Enhancing Cognitive Performance Through Strategic Auditory Stimulation</title>
      <dc:creator>Marcos Souza</dc:creator>
      <pubDate>Sun, 23 Mar 2025 14:40:00 +0000</pubDate>
      <link>https://dev.to/marcossouzadotdev/enhancing-cognitive-performance-through-strategic-auditory-stimulation-3o8i</link>
      <guid>https://dev.to/marcossouzadotdev/enhancing-cognitive-performance-through-strategic-auditory-stimulation-3o8i</guid>
      <description>&lt;p&gt;Research into cognitive enhancement methodologies reveals that carefully structured auditory environments can significantly impact focus, productivity, and mental endurance. After methodical experimentation with various sound combinations, a particularly effective configuration has emerged for achieving sustained concentration and flow states.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Optimal Auditory Environment
&lt;/h2&gt;

&lt;p&gt;The recommended configuration consists of three distinct layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gamma Wave Frequencies (30-100 Hz)&lt;/strong&gt;: Serving as the primary foundation, gamma waves have been associated with heightened cognitive processing. Research by Buzsáki and Wang (2012) indicates that gamma oscillations may support attention and working memory functions through neural synchronization across brain regions. These frequencies appear when the brain engages in complex information processing, suggesting their potential role in facilitating focused mental states.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Spatial White Noise&lt;/strong&gt;: This component provides background depth while effectively masking environmental distractions. According to studies by Söderlund et al. (2010), moderate noise can enhance cognitive performance by increasing arousal levels. The stochastic resonance phenomenon may explain how this background noise can paradoxically enhance signal detection in neural systems, effectively improving concentration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ambient Synthwave Music&lt;/strong&gt;: Introduced at significantly reduced volume (5-10% of the primary sounds), this element adds minimal rhythmic structure without becoming distracting. Research on music cognition by Zatorre and Salimpoor (2013) suggests that familiar, predictable musical patterns with limited vocal components can enhance task performance without competing for cognitive resources.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Neurological Mechanisms
&lt;/h2&gt;

&lt;p&gt;This acoustic combination appears effective through several complementary mechanisms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The gamma frequencies potentially stimulate cortical networks associated with focused attention and executive function&lt;/li&gt;
&lt;li&gt;White noise creates a consistent auditory mask that reduces the salience of unpredictable environmental sounds&lt;/li&gt;
&lt;li&gt;Low-volume synth-wave provides just enough rhythmic regularity to maintain optimal arousal without directing attention away from primary tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Observed Benefits
&lt;/h2&gt;

&lt;p&gt;Users implementing this specific combination report:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extended periods of sustained concentration&lt;/li&gt;
&lt;li&gt;Reduced sensitivity to environmental distractions&lt;/li&gt;
&lt;li&gt;Enhanced ability to achieve and maintain flow states during complex tasks&lt;/li&gt;
&lt;li&gt;Improved task completion rates without corresponding increases in mental fatigue&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation Recommendations
&lt;/h2&gt;

&lt;p&gt;For professionals seeking to enhance cognitive performance during demanding work sessions, consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using high-quality headphones to ensure proper frequency delivery&lt;/li&gt;
&lt;li&gt;Maintaining consistent volume levels across sessions&lt;/li&gt;
&lt;li&gt;Implementing this auditory environment during tasks requiring sustained attention and complex problem-solving&lt;/li&gt;
&lt;li&gt;Adjusting the synth-wave component volume based on individual sensitivity and task requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach represents a promising methodology for cognitive enhancement through non-invasive auditory stimulation. Further research into individualized configurations may yield additional insights into optimizing this approach for specific cognitive tasks.&lt;/p&gt;




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

&lt;p&gt;Buzsáki, G., &amp;amp; Wang, X. J. (2012). Mechanisms of gamma oscillations. Annual Review of Neuroscience, 35, 203-225.&lt;/p&gt;

&lt;p&gt;Söderlund, G., Sikström, S., &amp;amp; Smart, A. (2010). Listen to the noise: Noise is beneficial for cognitive performance in ADHD. Journal of Child Psychology and Psychiatry, 51(11), 1116-1122.&lt;/p&gt;

&lt;p&gt;Zatorre, R. J., &amp;amp; Salimpoor, V. N. (2013). From perception to pleasure: Music and its neural substrates. Proceedings of the National Academy of Sciences, 110(Supplement 2), 10430-10437.&lt;/p&gt;

</description>
      <category>productivityhacks</category>
      <category>cognitiveoptimization</category>
      <category>focustechniques</category>
      <category>professionaldevelopment</category>
    </item>
  </channel>
</rss>
