Perguntas de Entrevista
388 perguntas em 75 tópicos — clique numa pergunta para abrir a resposta no contexto do tópico
Cada pergunta tem âncora direta para a página do tópico. As respostas aqui são resumos do contexto completo que está na página de origem — clique no link "abrir no tópico ↗" para ver explicações, código, diagramas e perguntas relacionadas.
Padrões Distribuídos13
1Onde exatamente a ACL vive na Clean Architecture?ACL
Infrastructure layer. Domain define a interface (ex: IOrderRepository), Application usa a interface sem saber da implementação. Infrastructure implementa com a tradução ACL dentro. Se o schema legado mudar, você só muda a Infrastructure — Domain e Application ficam intactos.
abrir no tópico ↗ 2Diferença entre Outbox com polling vs Outbox com CDC?cdc-debezium
Polling tem um
abrir no tópico ↗ BackgroundService que faz SELECT periódico na tabela outbox, publica, e marca como Published. Latência 1–10s. CDC tem o Debezium a ler o WAL do banco — INSERT na outbox vira evento Kafka em <100ms, sem polling. CDC não precisa de coluna Published, e Kafka retém o log para replay. Polling é mais simples para começar; CDC é a evolução natural quando volume cresce ou latência vira problema.3O que acontece com um DELETE na tabela outbox em CDC?cdc-debezium
O Debezium publica um tombstone no Kafka — mensagem com key=X e value=null. Em tópicos compactados, o tombstone instrui o Kafka a apagar mensagens anteriores com aquela key. O consumer precisa tratar null explicitamente para não dar
abrir no tópico ↗ NullReferenceException. Boa prática: não fazer DELETE individual na outbox — usar particionamento por data (pg_partman) e dropar partições inteiras (DDL não gera tombstones).4Por que CDC é relevante para integração de sistemas legados?cdc-debezium
Porque permite capturar mudanças do sistema legado sem modificar o seu código. O legado continua a escrever no seu banco normalmente; o Debezium lê o WAL e propaga para o novo sistema. É o mecanismo perfeito para padrão Strangler Fig — migras gradualmente sem riscos no sistema antigo. Exemplo concreto: capturar mudanças do PLM legado e propagar para o knowledge graph sem o PLM ter que saber que o grafo existe.
abrir no tópico ↗ 5Por que o hash garante sticky behavior sem cookie ou sessão?Feature Flags
SHA256 é uma função pura — o mesmo input sempre produz o mesmo output. userId A sempre vai gerar o mesmo hashValue, então sempre vai pro mesmo sistema, sem precisar armazenar nada no cliente ou no servidor. É determinismo matemático.
abrir no tópico ↗ 6E se o userId for GUID — o hash ainda funciona?Feature Flags
Sim. O hash não precisa de número — ele opera nos bytes. GUID.ToByteArray() retorna 16 bytes, SHA256 processa qualquer sequência de bytes. A distribuição é idêntica a um int. A única diferença é que você não pode usar GetHashCode() porque ele não é determinístico entre máquinas.
abrir no tópico ↗ 7E se o relay publicar mas falhar antes de marcar como publicado?Outbox + Idempotência
A mensagem vai ser publicada de novo na próxima execução (at-least-once delivery). O consumidor vai receber duplicata. Por isso o consumidor precisa ser idempotente — detectar e ignorar eventos já processados.
abrir no tópico ↗ 8Por que jitter é obrigatório em sistemas distribuídos?Resilience
Sem jitter, clientes que falharam ao mesmo tempo têm delays idênticos — eles "sincronizam" involuntariamente e criam picos periódicos no serviço. Jitter quebra essa sincronização adicionando aleatoriedade ao delay. É a diferença entre uma multidão saindo de uma sala pela mesma porta ao mesmo tempo versus escalonada.
abrir no tópico ↗ 9Por que Circuit Breaker além do Retry?Resilience
Retry assume que o serviço vai se recuperar em segundos. Circuit Breaker assume que pode estar fora por muito mais tempo. No estado Open, retorna erro imediatamente — isso protege o serviço sobrecarregado de requests inúteis e protege o caller de esperar timeout em cada tentativa. São complementares, não substitutos.
abrir no tópico ↗ 10Qual usar numa migração de legado?Saga Pattern
Orchestration. Durante migração você precisa de visibilidade total e compensações explícitas. Com legado e novo no mesmo fluxo, o orquestrador serve como ponto central de auditoria. Choreography é difícil de debugar quando o fluxo está distribuído entre dois sistemas.
abrir no tópico ↗ 11Quando você sairia da Fase 1 para a Fase 2?Strangler Fig
Quando você tem confiança de que o novo sistema está correto. O sync validator job não deve estar logando discrepâncias, os testes de integração passam, e o time está confortável com o modelo de dados do novo sistema. Você não precisa de zero discrepâncias — você precisa de zero discrepâncias que importam para o negócio.
abrir no tópico ↗ 12O legado na Fase 2 fica com dados desatualizados?Strangler Fig
Sim — isso é eventual consistency deliberada. O legado vai ficar alguns segundos ou dezenas de segundos atrás do novo. Isso é aceitável se os consumidores do legado nessa fase toleram esse atraso. Se não tolerarem, você precisa encurtar o intervalo do SyncJob ou migrar esses consumidores para o novo sistema antes de sair da Fase 1.
abrir no tópico ↗ 13Como garantir que não há mais consumidores do legado antes de desligar?Strangler Fig
Um sinal não-amostrado nos endpoints do legado: contador de requests (métricas OpenTelemetry) ou access logs — tracing com sampling padrão pode mostrar zero spans mesmo com tráfego real, então use 100% de sampling nessas rotas. Se o contador ficar em zero por N dias, com N maior que o ciclo de negócio mais longo (pense em batch jobs trimestrais), é forte evidência de que não há consumidores. Também útil: headers Sunset (RFC 8594) e Deprecation (RFC 9745) para avisar consumidores com antecedência, e feature flags para desligar gradualmente por tenant em vez de um big bang de desligamento.
abrir no tópico ↗ Data Architecture9
1Diferença entre CP e AP em uma frase?cap-theorem
CP escolhe consistência sob partição — durante falha de rede, recusa-se a servir dados potencialmente desatualizados (timeout ou erro). AP escolhe disponibilidade — continua a servir mesmo que com dados potencialmente inconsistentes entre nós. CP prioriza "correto"; AP prioriza "respondendo". A escolha depende do custo de cada falha: ler stale vs ficar indisponível.
abrir no tópico ↗ 2Por que Redis é AP e não CP?cap-theorem
Redis prioriza disponibilidade e latência. Em cluster mode com replicação assíncrona, o master responde antes de confirmar nos replicas. Em caso de partição, replicas podem servir dados ligeiramente stale. Para cache isto é aceitável — se o cache der dados antigos, o pior caso é um re-fetch. Para source of truth não seria aceitável. Por isso Redis é cache, não fonte de verdade — a escolha PA/EL bate com seu uso.
abrir no tópico ↗ 3MongoDB é CP ou AP?cap-theorem
Depende da configuração. Por default com
abrir no tópico ↗ w: "majority" e readConcern: "majority", MongoDB é CP — espera maioria das réplicas confirmarem antes de retornar. Com read concern local e w: 1, fica AP. É configurável por operação — você pode ter uma collection sendo CP para writes financeiros e AP para reads analíticos. O importante na entrevista é mostrar que conhece a granularidade da escolha.4Como escolherias o banco para um sistema de recomendação que precisa responder em <10ms para 1M usuários?persistence-strategy
Provavelmente uma combinação. Redis (ou ElastiCache) como cache de hot data para garantir os <10ms. Por baixo, o source of truth pode ser SQL (se as recomendações são derivadas de transações), Graph (se são baseadas em relações entre usuários e itens), ou Document (se são pré-computadas e armazenadas como blob por usuário). A escolha do source of truth depende do padrão de geração das recomendações, não da latência de leitura — essa resolve-se sempre com cache. Veja também polyglot persistence.
abrir no tópico ↗ 5SQL ou NoSQL para um sistema de pagamentos?persistence-strategy
SQL, quase sempre. Pagamentos exigem ACID — transações atômicas, consistência forte, auditoria. NoSQL com eventual consistency pode causar double-charges ou estados inconsistentes. NoSQL pode complementar (event log em Kafka, cache em Redis), mas o source of truth de uma transação financeira deve ser ACID. Veja também CAP theorem: pagamento é caso clássico de CP — em partição você prefere recusar a aceitar inconsistência.
abrir no tópico ↗ 6Quando faz sentido aceitar eventual consistency?persistence-strategy
Quando o usuário tolera ver dados ligeiramente desatualizados em troca de disponibilidade e performance. Exemplos: feeds de redes sociais (alguns segundos de atraso são imperceptíveis), contadores de likes (precisão aproximada é OK), recommendation engines (o usuário não sabe o que "deveria" ter aparecido). Quando NÃO: saldo de conta, estoque disponível, qualquer coisa onde "ler stale" pode causar decisão errada do usuário. Regra prática: se ler stale pode causar perda monetária ou decisão irreversível, exige consistência forte.
abrir no tópico ↗ 7Quais os principais riscos de polyglot persistence?polyglot-persistence
Três principais. (1) Consistência eventual entre stores — o write em SQL pode estar visível antes do read model em Redis ser atualizado, causando confusão do usuário. (2) Complexidade operacional — mais bancos para monitorar, backups, schemas, migrations. (3) Skill diversity — a equipe precisa saber operar todos. A mitigação é começar simples e adicionar stores apenas quando o ganho compensa o custo.
abrir no tópico ↗ 8Como manter polyglot persistence consistente?polyglot-persistence
Via eventos. O write model publica eventos (via Outbox), e os read models são projeções desses eventos. Cada consumer atualiza o seu store. Isto é eventual consistency — os stores convergem mas não simultaneamente. Para operações que precisam de consistência forte, lê do source of truth, não dos read models. Para Read-Your-Own-Writes, sticky session ao primário ou cache local de "writes recentes".
abrir no tópico ↗ 9Quando polyglot persistence é over-engineering?polyglot-persistence
Quando o sistema cabe num banco e a equipe não tem capacity operacional para mais. Um MVP com 10k usuários não precisa de 4 bancos especializados. PostgreSQL com JSONB columns e full-text search nativo cobre 80% dos casos. Polyglot justifica-se quando o volume ou os padrões de acesso já estão a forçar trade-offs no banco único — e tem equipe para gerir o resto. Comece com Postgres + Redis; cresça por necessidade comprovada.
abrir no tópico ↗ Side Projects3
1Conta-me sobre um projeto pessoal que tenhas orgulhomtg-engine
"Construí um knowledge graph engine para analisar decks de MTG Commander. O problema que queria resolver era cruzar dois sinais — popularidade (do EDHREC) e performance competitiva (de torneios) — porque um não implica o outro. Usei dotNetRDF para construir triplas em C#, SHACL para validar a ingestão, Fuseki como triplestore local e ASP.NET Core para expor queries SPARQL via REST. A arquitetura escala para Neptune sem mudanças significativas — só troco o endpoint. Posso passar por qualquer parte que vos interesse — ontologia, queries, ingestão, deploy."
abrir no tópico ↗ 2Por que RDF nesse projeto e não Neo4j?mtg-engine
Três razões. (1) Vocabulários públicos: queria poder alinhar com Wikidata e schema.org no futuro, e RDF é a língua franca. (2) Validação semântica: SHACL é mais expressivo que constraints do Cypher para regras de domínio complexas (color identity, banlist). (3) Trade-off de adoção: já uso .NET para tudo, e dotNetRDF é maduro; meter Neo4j adicionaria stack sem ganho proporcional. Se a dor fosse pathfinding intenso, eu repensaria — Neo4j ganha em performance bruta de travessia. Veja RDF vs LPG.
abrir no tópico ↗ 3Como você priorizou as fases do roadmap?mtg-engine
Por risco técnico × valor de domínio. Fase 1 valida que RDF + SPARQL realmente respondem o que queria perguntar — sem isso, o resto é fantasia. Fase 2 prova que o pipeline aguenta dados reais sob volume razoável. Fase 3 (AI) só vale depois que o grafo é confiável — RAG sobre dado ruim é "garbage in, garbage out". Fase 4 (AWS) é puro derisking de operação — não muda funcionalidade. Princípio: cada fase tem um critério de "pronto" mensurável; só passo pra próxima quando o anterior responde a uma pergunta de negócio real.
abrir no tópico ↗ DevOps & Deployment5
1Para o sistema de pagamentos da Flex, que estratégia de release escolherias?release-strategies
Para um sistema de pagamentos, o default seria canary, assumindo que temos a observability necessária. Quero detectar problemas com o menor blast radius possível — se há um bug na versão nova, prefiro afetar 5% dos pagamentos do que 100%. Mas canary só funciona se temos SLOs definidos e rollback automático baseado em métricas — senão é apenas "esperar que alguém repare a tempo". Se a equipe não está pronta para infra de canary, blue-green é a próxima escolha — rollback menos rápido que canary, mas instantâneo comparado com rolling. Rolling deixaria para serviços menos críticos onde a tolerância a downtime é maior. E qualquer estratégia exige que schema migrations sejam backward-compatible — durante o canary ou no rollback do blue-green, código antigo precisa funcionar com schema novo.
abrir no tópico ↗ 2Diferença essencial entre blue-green e canary?release-strategies
Blue-green é um switch — 100% v1, depois 100% v2, instantâneo. Canary é progressivo — 5%, 20%, 50%, 100%, observando métricas em cada step. Blue-green é melhor para rollback rápido em sistemas críticos. Canary é melhor para detectar bugs com blast radius pequeno antes de full rollout. Custo: blue-green exige 2× infra, canary precisa de service mesh para roteamento ponderado. Ambos exigem backward-compat de schema; o canary mais, porque a janela de coexistência é maior.
abrir no tópico ↗ 3Por que canary precisa de observability?release-strategies
Porque toda a premissa do canary é "detectar problemas com 5% do tráfego antes de afetar 100%". Sem métricas confiáveis (error rate, latency p99, business metrics como conversion), não dá pra decidir se promover ou fazer rollback — só está esperando alguém reportar. SLOs claros permitem automatizar: ferramentas como Flagger fazem rollout progressivo e rollback automático baseado em métricas do Prometheus. Sem isso, canary é apenas uma versão mais lenta do rolling update. Veja SLI/SLO.
abrir no tópico ↗ 4Se você pode fazer Canary, por que escolheria Blue-Green?release-strategies
Três razões. (1) Simplicidade — blue-green não precisa de service mesh nem de SLOs maduros, qualquer K8s ou load balancer suporta. (2) Breaking changes — se v1 e v2 são totalmente incompatíveis (ex: protocolo de API mudou), não dá pra ter os dois servindo tráfego simultaneamente, e canary exige isso. (3) Validação pré-traffic — em blue-green você pode fazer smoke tests completos no green antes de qualquer usuário real ver. Em canary, o "teste" é o tráfego real.
abrir no tópico ↗ 5Como lidar com database migrations em canary?release-strategies
Schema novo precisa funcionar com código antigo (v1) durante o canary, porque v1 ainda está servindo 95%/80%/50% do tráfego. Padrão expand-and-contract: primeira migration adiciona estrutura nova nullable, código v2 escreve em ambas, v1 continua a ler do antigo. Só depois de v2 estar em 100% e estável (dias/semanas) é que dropa a estrutura antiga numa migration separada. Migrations destrutivas (DROP, ALTER NOT NULL) nunca devem coincidir com canary — são deploys separados, isolados.
abrir no tópico ↗ C# Fundamentals35
1Por que usar generics em vez de ArrayList?Collections & Generics
Tres razoes: (1) Type safety em compile-time -- erros de tipo sao pegos antes de rodar. (2) Zero boxing para value types -- ArrayList faz box/unbox a cada operacao. (3) Performance -- menos alocacoes, menos pressao no GC, e o JIT pode otimizar melhor com tipos concretos.
abrir no tópico ↗ 2Quando preferir Array sobre List<T>?Collections & Generics
Quando o tamanho e fixo e conhecido em compile-time, em hot paths criticos de performance (evita overhead de bounds checking do List), ou em cenarios de interop com APIs nativas. No dia-a-dia,
abrir no tópico ↗ List<T> e quase sempre a melhor escolha pela flexibilidade.3O que acontece se voce mutar um campo usado no GetHashCode de uma chave que ja esta no Dictionary?Collections & Generics
O hash muda, mas a chave continua no bucket antigo. O Dictionary nunca mais encontra a chave --
abrir no tópico ↗ ContainsKey retorna false, TryGetValue falha, e voce tem um memory leak efetivo. Por isso, chaves devem ser imutaveis (use readonly struct ou record).4Qual a diferenca entre Queue e Channel no contexto de producers/consumers?Collections & Generics
Queue<T> nao e thread-safe e nao suporta await. ConcurrentQueue<T> e thread-safe mas nao tem backpressure. Channel<T> combina concorrencia com backpressure (bounded channels) e async/await nativo -- e a melhor escolha para pipelines producer/consumer em .NET moderno.5Por que Dictionary e O(1) e nao O(n)?Collections & Generics
Dictionary usa uma hash table. A chave passa por
abrir no tópico ↗ GetHashCode() que gera um indice direto para o bucket -- acesso direto sem varrer todos os elementos. Isso e amortizado O(1) porque, com boa distribuicao de hash, cada bucket tem poucos (ou um) elemento. No pior caso (todas as chaves no mesmo bucket), degrada para O(n), mas isso so acontece com implementacoes de hash ruins.6Quando usar delegate customizado vs Func/Action?Delegates & Events
Prefira
abrir no tópico ↗ Func/Action para assinaturas comuns. Use delegate customizado apenas quando o nome comunica intencao do dominio ou precisa de XML-doc especifico. Ex: delegate bool IntPredicate(int x) adiciona clareza semantica.7Por que event e nao apenas um delegate publico?Delegates & Events
Sem
abrir no tópico ↗ event, qualquer classe poderia sobrescrever (=) ou invocar o delegate. O event garante encapsulamento: so o dono invoca, externos so assinam/desassinam.8O que e o snapshot var h = Message antes do Invoke?Delegates & Events
E thread-safety. Se um subscriber faz
abrir no tópico ↗ -= entre o null-check e o Invoke, voce teria NullReferenceException. Copiar para variavel local garante que a referencia nao muda.9Como garantir que falha em um subscriber nao derruba os outros?Delegates & Events
Isole cada handler com try/catch. No pattern async, use
abrir no tópico ↗ InvokeSafe que faz await individual com catch. Com Task.WhenAll, cada task ja esta isolada. Nunca deixe excecoes propagarem para o publisher sem tratamento.10Por que imutabilidade e importante em C#?Immutability
Imutabilidade torna objetos thread-safe por padrao, elimina bugs de aliasing, garante que hash codes permanecam estaveis para uso como chaves de dicionario, e facilita o raciocinio sobre o codigo. Em sistemas distribuidos, snapshots imutaveis permitem leituras sem lock e publicacao atomica de novos estados.
abrir no tópico ↗ 11Qual a diferenca entre record e class para imutabilidade?Immutability
Records tem igualdade por valor,
abrir no tópico ↗ with expressions e deconstruction por padrao. Classes usam igualdade por referencia. Para domain models imutaveis, records sao a escolha natural — mas lembre que record class ainda aloca no heap. Para value types pequenos, use record struct ou readonly struct.12Quando usar StringBuilder em vez de concatenacao?Immutability
Use StringBuilder quando concatena em loops ou quando o numero de partes e desconhecido/grande. Para 2-3 concatenacoes fixas numa unica expressao, concatenacao normal e mais legivel e o compilador/JIT gera codigo eficiente. A regra pratica: se voce nao consegue ver todas as partes numa unica linha, use StringBuilder.
abrir no tópico ↗ 13O que e string interning e quando usar?Immutability
String interning e o mecanismo do CLR que armazena uma unica copia de cada string literal no intern pool. Literais sao internados automaticamente; strings dinamicas nao. Use
abrir no tópico ↗ string.Intern quando voce tem strings repetidas de longa duracao (ex: nomes de colunas, roles). Evite para dados de alta cardinalidade — o pool nunca e coletado pelo GC.14Como imutabilidade ajuda em concorrencia?Immutability
Objetos imutaveis sao thread-safe por natureza — se o estado nunca muda, nao ha race conditions. Para atualizacoes, use o padrao copy-on-write: crie uma nova instancia com as mudancas e publique atomicamente via
abrir no tópico ↗ Interlocked.CompareExchange ou Volatile.Write. Leitores sempre veem um snapshot consistente sem precisar de locks.15Por que nao colocar tudo numa interface com default methods?Interfaces vs Abstract
Default interface methods (C# 8+) nao podem acessar estado (campos de instancia). Abstract classes podem ter campos, construtores e protected helpers. Alem disso, default methods adicionam complexidade (diamond problem) e nao sao invocaveis diretamente pela classe implementadora — apenas via cast para a interface.
abrir no tópico ↗ 16C# suporta heranca multipla de classes?Interfaces vs Abstract
Nao. Uma classe pode herdar de apenas uma classe base, mas pode implementar quantas interfaces quiser. Interfaces modelam capacidades ortogonais (IHasPerimeter, IRenderable), enquanto a abstract class centraliza logica compartilhada.
abrir no tópico ↗ 17Quando usar struct com interface ao inves de class?Interfaces vs Abstract
Cuidado com boxing! Quando um value type (struct) e atribuido a uma variavel de tipo interface, ocorre boxing — alocacao no heap. Em hot paths, isso pode gerar pressao no GC. Prefira generics com constraints (
abrir no tópico ↗ where T : IShape) para evitar boxing.18Por que o Template Method precisa de abstract class e nao interface?Interfaces vs Abstract
Porque o Template Method implementa o algoritmo na base (Export chama Validate, Format, Write em sequencia). Interfaces nao podem ter implementacao com estado compartilhado (FileName, constructor). Default interface methods sao limitados — nao acessam campos de instancia e nao podem ser sealed.
abrir no tópico ↗ 19Qual a diferenca entre virtual e abstract num metodo?Interfaces vs Abstract
abstract: a classe derivada DEVE implementar (obrigatorio). virtual: a base fornece implementacao padrao, derivada PODE fazer override (opcional). No Template Method, Validate e Write sao virtual (tem default), Format e abstract (cada exporter DEVE definir).
abrir no tópico ↗ 20O que acontece se duas interfaces fornecem default methods com mesma assinatura?Interfaces vs Abstract
O compilador da erro — a classe implementadora deve fornecer sua propria implementacao explicitamente. E o "diamond problem" do C#. Por isso default interface methods devem ser usados com cautela, especialmente em APIs publicas.
abrir no tópico ↗ 21Qual a vantagem de abstract class para versionamento?Interfaces vs Abstract
Voce pode adicionar um virtual member com default e nenhum herdeiro quebra. Se adicionar um abstract member, e breaking — mas pelo menos voce tem controle total da hierarquia. Com interfaces em APIs publicas, voce nao controla quem implementa.
abrir no tópico ↗ 22Qual a diferenca entre virtual e abstract?Polymorphism
abstract nao tem corpo — forca a classe derivada a implementar.
virtual tem implementacao padrao — a classe derivada pode opcionalmente fazer override.
Ambos participam de virtual dispatch (polimorfismo runtime).23O que acontece se eu chamar um metodo virtual no construtor da classe base?Polymorphism
O virtual dispatch ja esta ativo, entao o metodo da classe derivada e chamado — mas o construtor da derivada ainda nao rodou.
Isso pode causar bugs porque o objeto esta em estado inconsistente. Evite chamar metodos virtuais em construtores.
abrir no tópico ↗ 24Por que sealed override melhora performance?Polymorphism
Com
abrir no tópico ↗ sealed override, o JIT sabe que nao existem mais overrides possiveis. Isso permite
devirtualization — o JIT substitui a chamada virtual por uma chamada direta, eliminando a indirection
da vtable. Em hot paths, isso pode ter impacto mensuravel.25O que sealed override faz?Polymorphism
Faz override do metodo virtual/abstract da classe pai, mas impede que classes filhas facam override novamente.
No exemplo,
abrir no tópico ↗ Dog faz sealed override de Speak(), entao LoudDog nao pode
fazer override — apenas new (que nao e polimorfismo).26Quando devo usar new em vez de override?Polymorphism
Quase nunca.
abrir no tópico ↗ new esconde o membro da classe base, criando um segundo metodo com o mesmo nome.
O comportamento muda dependendo se a variavel e do tipo base ou derivado — isso e confuso e propenso a bugs.
Use apenas quando intencionalmente precisa de shadowing (raro).27Interface ou classe abstrata — como escolher?Polymorphism
Interface quando o contrato define uma capacidade (IDisposable, IComparable, IArea).
Abstract class quando existe comportamento compartilhado e relacao hierarquica real.
Em C# uma classe so pode herdar de uma, mas implementar muitas interfaces.
Na duvida, comece com interface — e mais flexivel.
abrir no tópico ↗ 28Default Interface Methods (C# 8+) mudam essa regra?Polymorphism
Parcialmente. Com default interface methods, interfaces podem ter implementacao padrao.
Mas nao podem ter estado (campos de instancia). Sao uteis para adicionar metodos a interfaces
existentes sem quebrar implementacoes. Nao substituem abstract classes quando estado compartilhado e necessario.
abrir no tópico ↗ 29Pattern matching e considerado polimorfismo?Polymorphism
Sim, e uma forma de ad-hoc polymorphism. Em vez de dispatch automatico via vtable,
voce faz dispatch explicito via switch/is. O compilador ajuda com exhaustiveness checks.
E idiomatico em C# moderno para trabalhar com records e discriminated unions.
abrir no tópico ↗ 30Como o Strategy pattern se relaciona com polimorfismo?Polymorphism
Strategy e polimorfismo aplicado. O "contexto" (PricingEngine) mantem uma referencia
para a abstracao (IPricingRule). Em runtime, o comportamento concreto vem da implementacao injetada.
Trocar a estrategia nao requer mudanca no engine — isso e o Open/Closed Principle.
abrir no tópico ↗ 31Delegate vs Interface para Strategy — quando usar cada?Polymorphism
Delegate (Func/Action): quando a estrategia e um unico metodo e nao precisa de estado interno.
Mais leve, permite lambdas inline.
Interface: quando precisa de multiplos metodos (Applies + Apply), estado interno, ou quer registrar no container de DI com lifetime management.
abrir no tópico ↗ Interface: quando precisa de multiplos metodos (Applies + Apply), estado interno, ou quer registrar no container de DI com lifetime management.
32Por que usar record em vez de class com Equals override?Records & Patterns
Records sintetizam
abrir no tópico ↗ Equals, GetHashCode, Deconstruct
e ToString automaticamente. Voce ganha igualdade por valor, pattern matching posicional
e with sem escrever boilerplate. Menos codigo, menos bugs.33O with faz deep copy ou shallow copy?Records & Patterns
Shallow copy — ele copia as referencias para propriedades de tipo referencia.
Para nested records, compose
abrir no tópico ↗ with:
u1 with { Addr = u1.Addr with { Street = "Rue 2" } }.
Cada nivel precisa do seu proprio with.34Qual a regra de ouro para ordenar arms num switch expression?Records & Patterns
Do mais especifico para o mais geral. O compilador avalia de cima para baixo —
um arm geral antes de um especifico torna o especifico inalcancavel.
Sempre inclua
abrir no tópico ↗ _ no final para garantir exhaustiveness.35Por que usar abstract record em vez de interface para union types?Records & Patterns
Abstract record da igualdade por valor,
abrir no tópico ↗ with, ToString legivel
e pattern matching posicional de graca. Com interface voce teria que implementar
tudo manualmente. Alem disso, records suportam heranca entre si,
permitindo hierarquias fechadas e type-safe.Async & Performance26
1Por que nao se deve usar async void?Async/Await
async void nao retorna Task, entao o chamador nao pode awaitar nem observar excecoes.
Se uma excecao ocorre, ela vai para o SynchronizationContext e geralmente crasha o processo.
A unica excecao e em event handlers (ex: Button_Click), onde a assinatura e imposta pelo framework.
Em qualquer outro caso, retorne Task.2O que acontece se voce esquecer de awaitar uma Task?Async/Await
A Task roda em "fire-and-forget" — excecoes sao silenciosamente engolidas (nao propagam para o chamador).
Em .NET 4+, Tasks nao observadas nao crasham o processo, mas voce perde o controle de erros, ordering e lifetime.
O compilador emite warning CS4014 para alertar. Sempre awaite ou armazene a Task explicitamente com descarte (
abrir no tópico ↗ _ = DoAsync()).3ConfigureAwait(false) — quando e por que?Async/Await
Por padrao,
abrir no tópico ↗ await captura o SynchronizationContext e resume no mesmo contexto (ex: UI thread).
ConfigureAwait(false) pula essa captura — a continuacao roda em qualquer thread do pool.
Use em codigo de biblioteca para evitar deadlocks e reduzir overhead.
ASP.NET Core nao tem SynchronizationContext de jeito nenhum — ConfigureAwait(false) dentro de uma controller/minimal API e um no-op (nao quebra nada, so nao faz nada).
Em WPF/WinForms/MAUI/ASP.NET Framework, sem ConfigureAwait(false) em bibliotecas chamadas com .Result/.Wait() voce deadlocka: a continuacao quer voltar ao contexto original, que esta bloqueado esperando o resultado.
Em bibliotecas redistribuidas, sempre use ConfigureAwait(false) — voce nao sabe quem vai chamar.4Qual a diferenca entre retornar Task<List<T>> e IAsyncEnumerable<T>?Async/Await
Task<List<T>> espera todos os itens estarem prontos antes de retornar — consome mais memoria e tem maior latencia para o primeiro item.
IAsyncEnumerable<T> produz itens sob demanda via yield return — o consumidor recebe o primeiro item assim que estiver pronto.
Ideal para paginacao, streaming e cenarios onde o volume total e grande ou desconhecido.5Qual e o perigo de enumerar multiplas vezes (multiple enumeration)?LINQ & Deferred
Cada enumeracao re-executa todo o pipeline. Se a fonte e uma query de banco, voce faz N queries ao inves de 1. Se a fonte produz side-effects (logging, timers), eles se repetem. A solucao e materializar com
abrir no tópico ↗ ToList() e usar a lista materializada para Sum(), Count(), etc. Alem disso, se os dados mudarem entre enumeracoes, os resultados serao inconsistentes.6Quando usar IQueryable vs IEnumerable?LINQ & Deferred
Use
abrir no tópico ↗ IQueryable<T> enquanto estiver construindo queries contra fontes remotas (banco, API) — isso garante que filtros, ordenacao e projecoes sejam traduzidos para SQL e executados no servidor. Troque para IEnumerable<T> (via AsEnumerable() ou ToList()) quando precisar de logica que nao e traduzivel para SQL (metodos C# customizados, formatacao de strings complexa). A regra e: empurre o maximo de trabalho pro servidor e traga o minimo de dados pro cliente.7Qual a diferenca entre db.Cards.Where(c => c.Price < 5).ToList() e db.Cards.AsEnumerable().Where(c => c.Price < 5).ToList() em EF Core?LINQ & Deferred
A primeira executa
abrir no tópico ↗ SELECT ... WHERE Price < 5 — o filtro vai para o SQL, so as linhas que importam atravessam a rede. A segunda chama AsEnumerable() primeiro, o que materializa cedo demais: o EF emite SELECT * FROM Cards, traz todas as linhas para a memoria do C#, e so depois aplica o Where em LINQ to Objects. Em uma tabela com milhoes de linhas isso e a diferenca entre uma query rapida e um OOM. AsEnumerable() so deve aparecer depois de tudo que da pra traduzir, quando voce precisa rodar logica C# que o provider nao entende.8Qual e o perigo de enumerar multiplas vezes (multiple enumeration)?LINQ & Deferred
Cada enumeracao re-executa o pipeline inteiro. Com fonte cara (DB, API), voce paga o custo N vezes. Se dados mudam entre enumeracoes, resultados ficam inconsistentes. Solucao:
abrir no tópico ↗ var list = pipeline.ToList(); e use list para todas as operacoes subsequentes.9Como funciona stackalloc?Memory & Span
stackalloc reserva N bytes no stack frame do metodo atual. Nao passa pelo GC. O buffer e liberado quando o metodo retorna (stack pointer volta). Use para buffers pequenos e de curta duracao. Se o buffer for grande demais, causa StackOverflowException.
abrir no tópico ↗ 10Por que Span nao pode ser armazenado no heap?Memory & Span
Span<T> e um ref struct - vive exclusivamente na stack. Se fosse armazenado no heap (campo de classe, boxed, closure), o GC poderia mover o objeto que contem o Span, mas o ponteiro interno do Span nao seria atualizado - causando dangling pointer. A restricao garante que o Span sempre aponta para memoria valida durante seu lifetime (escopo do metodo).
abrir no tópico ↗ 11Quando usar Memory<T> vs Span<T>?Memory & Span
Use Span<T> quando o processamento e sincrono e nao precisa escapar do metodo - e mais rapido. Use Memory<T> quando precisa armazenar a referencia em um campo, passar para metodo async, ou usar em pipeline (System.IO.Pipelines). Memory<T> e um struct normal que pode viver no heap; para acessar os dados, chame .Span dentro de um escopo sincrono.
abrir no tópico ↗ 12Como stackalloc funciona?Memory & Span
stackalloc reserva memoria no stack frame atual - nao no heap. A alocacao e instantanea (apenas move o stack pointer). A memoria e liberada automaticamente quando o metodo retorna. Nao passa pelo GC. Limite pratico: poucos KB; buffers grandes causam StackOverflowException. Sempre use com Span<T> para ter bounds checking.
abrir no tópico ↗ 13Por que arrays grandes podem causar "memory leaks" mesmo com GC?Memory & Span
Arrays >= 85 KB vao para o Large Object Heap, que so e coletado junto com Gen 2 e nao e compactado por padrao. Se voce aloca e descarta varios arrays grandes em hot path, o LOH fragmenta — voce acaba com muito espaco "livre" entre objetos vivos, mas nenhum bloco contiguo grande o suficiente para a proxima alocacao. O processo cresce ao longo do tempo mesmo com working set "limpo". Solucao:
abrir no tópico ↗ ArrayPool<T>.Shared.Rent reusa o mesmo array em vez de criar/descartar.14Qual a diferenca entre Workstation GC e Server GC?Memory & Span
Workstation GC tem um heap unico e coleta na thread que disparou a alocacao — bom para apps de UI (latencia baixa, footprint menor). Server GC mantem um heap por logical core e coleta em threads dedicadas paralelas — bom para apps de servidor com muitos cores (throughput maior, pausas menos previsiveis individualmente, mas vazao total muito maior). ASP.NET Core liga Server GC por padrao.
abrir no tópico ↗ 15Quando usar Memory<T> em vez de Span<T>?Memory & Span
Span<T> e um ref struct — so vive na stack, nao pode ser campo de classe, nao pode atravessar await ou yield. Memory<T> e um struct comum: pode ser passado para metodos async, virar campo de uma classe e atravessar fronteiras de continuacao. Use Memory<T> em APIs publicas async (ex: Stream.ReadAsync(Memory<byte>)) e converta para Span<T> via .Span apenas dentro de metodos sync hot path.16Quando NAO paralelizar?Parallel
1) Quando o trabalho e I/O-bound — threads ficam bloqueadas esperando, nao processando. Use async/await.
2) Quando a colecao e pequena — o overhead de criar partitions e coordenar threads e maior que o ganho.
3) Quando os itens tem dependencias entre si — paralelismo assume independencia.
4) Quando voce esta dentro de um request handler ASP.NET — o ThreadPool ja esta compartilhado; paralismo compete com outros requests.
abrir no tópico ↗ 17Parallel.ForEach vs Task.WhenAll?Parallel
Parallel.ForEach e para CPU-bound — usa threads do ThreadPool para processar itens em paralelo.
Task.WhenAll e para I/O-bound — agenda tasks async que nao bloqueiam threads enquanto esperam I/O.
Misturar os dois e um erro comum: usar Parallel.ForEach para chamadas HTTP bloqueia threads desnecessariamente.
Para I/O com limite de concorrencia, use
abrir no tópico ↗ Task.WhenAll + SemaphoreSlim.18Como faria 100 chamadas HTTP em paralelo com no maximo 10 simultaneas?Parallel
A forma idiomatica em .NET 6+ e
abrir no tópico ↗ Parallel.ForEachAsync com ParallelOptions.MaxDegreeOfParallelism = 10, recebendo uma delegate async (id, ct) => await http.FetchAsync(id, ct). Ele gerencia o limite sem bloquear threads, integra CancellationToken, e propaga excecoes via AggregateException. Antes de .NET 6, a forma era Task.WhenAll sobre uma sequencia de tasks que adquirem um SemaphoreSlim(10) antes de chamar FetchAsync e liberam no finally. Nunca use Parallel.ForEach sincrono para isso — ele bloqueia uma thread por chamada HTTP enquanto espera I/O, esgotando o thread pool.19Por que ConcurrentBag<T> e ruim para producer/consumer mas otimo dentro de Parallel.ForEach?Parallel
ConcurrentBag usa um buffer thread-local por produtor e faz work stealing quando um consumer pega itens. Dentro de Parallel.ForEach, cada thread do pool adiciona em seu proprio buffer — sem contencao, sem ordem importando. Em producer/consumer real (uma thread produz, outra consome), os buffers thread-local viram desvantagem: o consumer precisa roubar de cada produtor, latencia maior que ConcurrentQueue. Use ConcurrentBag apenas para "acumular resultados em paralelo sem se importar com ordem".20PLINQ ordering guarantees?Parallel
Por padrao, PLINQ nao preserva ordem — itens podem chegar em qualquer sequencia.
Use
abrir no tópico ↗ AsOrdered() para manter a ordem do input, mas com custo de performance (buffer + coordenacao).
Apos uma secao ordenada, AsUnordered() libera a restricao para operacoes seguintes.
ForAll() ignora ordem completamente — executa action direto nas threads sem merge.21Como funciona AggregateException em Parallel.ForEach?Parallel
Quando multiplos itens falham em paralelo, cada excecao e capturada individualmente. Ao final, o runtime empacota todas em uma
abrir no tópico ↗ AggregateException.
Use ae.Flatten() para achatar AggregateExceptions aninhadas e .InnerExceptions para iterar sobre cada falha individual.
Isso e diferente de um loop sequencial onde a primeira excecao para a execucao — em paralelo, as outras threads podem ja ter iniciado trabalho.22Por que preferir Task.Run() a new Thread()?Threading
Task.Run() usa o ThreadPool, que reutiliza threads ja existentes — sem custo de criacao/destruicao do OS. Alem disso, Task compoe com async/await, suporta CancellationToken, e propaga excecoes de forma estruturada. Thread bruto so faz sentido para cenarios especiais como threads STA ou loops de longa duracao dedicados.
abrir no tópico ↗ 23lock vs SemaphoreSlim?Threading
lock e exclusao mutua binaria (0 ou 1) e so funciona synchronous — nao tem await dentro de um lock. SemaphoreSlim suporta N slots de concorrencia e tem WaitAsync() para uso com async/await. Use lock para secoes criticas curtas e synchronous; SemaphoreSlim para limitar concorrencia async ou quando precisa de mais de 1 slot.24Quando usar SemaphoreSlim(1,1) vs lock?Threading
SemaphoreSlim(1,1) e funcionalmente similar ao lock, mas suporta await WaitAsync(). Use quando precisar de exclusao mutua dentro de metodos async. O lock nao pode conter await porque o compilador nao garante que Enter e Exit executam na mesma thread.25ConcurrentDictionary thread safety caveats?Threading
Operacoes individuais (
abrir no tópico ↗ GetOrAdd, TryAdd, TryRemove) sao atomicas. Mas sequencias de operacoes nao sao — "check then act" continua sendo race condition. A factory de GetOrAdd pode executar mais de uma vez para a mesma key (so o resultado de uma e inserido). Para factories caras, use GetOrAdd(key, _ => new Lazy<T>(factory)).Value.26What's a deadlock and how to prevent?Threading
Deadlock ocorre quando duas ou mais threads ficam permanentemente bloqueadas, cada uma esperando um recurso que a outra segura. Prevencao: (1) adquira locks sempre na mesma ordem global, (2) use timeouts com
abrir no tópico ↗ Monitor.TryEnter ou SemaphoreSlim.WaitAsync(timeout), (3) minimize o escopo de locks, (4) prefira colecoes concorrentes e Interlocked a locks manuais.Architecture & Design40
1Quando CQRS vale a complexidade extra?CQRS & Event Sourcing
CQRS vale quando: 1) Read e write tem requisitos de escala muito diferentes.
2) O modelo de dominio e complexo e voce quer proteger invariantes no write side.
3) Voce precisa de projecoes otimizadas para diferentes consumidores (API, relatorio, busca).
Nao vale para CRUDs simples — a complexidade adicional nao se justifica.
abrir no tópico ↗ 2Como voce lida com evolucao de schemas de eventos?CQRS & Event Sourcing
Tres estrategias principais: 1) Upcasting — transforme o evento antigo para o formato novo no momento da leitura. O evento original permanece intacto no store.
2) Weak schema — use JSON flexivel e trate campos ausentes com defaults no handler.
3) New event type — crie um novo tipo de evento (OrderCreatedV2) e mantenha handlers para ambas as versoes.
Na pratica, upcasting e o mais comum com EventStoreDB. Nunca altere eventos existentes — isso viola a imutabilidade do event store.
abrir no tópico ↗ 3MediatR e necessario para CQRS?CQRS & Event Sourcing
Nao. MediatR e conveniencia, nao requisito. Voce pode usar DI puro com
abrir no tópico ↗ ICommandHandler<T> e IQueryHandler<TQuery, TResult>.
MediatR facilita com discovery automatico, pipeline behaviors e convencao padronizada.
O risco e over-engineering: se todo handler so chama um repositorio, MediatR adiciona indirection sem beneficio.4Como voce lida com eventual consistency entre write e read models?CQRS & Event Sourcing
1) Read-your-writes: retorne o dado criado diretamente no response do command.
2) Causal consistency: inclua version no response; frontend so consulta quando read model atingir essa version.
3) UI optimistic update: atualize a UI imediatamente, reconcilie quando o read model atualizar.
4) Subscriptions: use SignalR/WebSocket para push. Na maioria dos casos, o delay e de milissegundos.
abrir no tópico ↗ 5Como DI se relaciona com o Dependency Inversion Principle (SOLID)?Dependency Injection
O Dependency Inversion Principle diz que modulos de alto nivel nao devem depender de modulos de baixo nivel - ambos devem depender de abstracoes.
DI e o mecanismo que implementa esse principio: o container injeta a implementacao correta da abstracao no construtor.
abrir no tópico ↗ 6Se em dúvida entre Scoped e Transient, qual escolher por default?Dependency Injection
Scoped. Em web API, um scope é uma request — não há ganho real em ter
mais de uma instância de um service stateless por request, e Scoped previne bugs sutis
(como dois handlers no mesmo request com state divergente).
Use Transient só quando o ciclo de vida do service é genuinamente menor que o request
(ex: builder pattern usado e descartado dentro de um método).
abrir no tópico ↗ 7O que e o problema de Captive Dependency?Dependency Injection
Ocorre quando um servico com lifetime mais longo (Singleton) injeta diretamente um servico com lifetime mais curto (Scoped/Transient).
O servico "capturado" vive mais do que deveria, compartilhando estado entre requests.
Solucao: use
abrir no tópico ↗ Func<T> (factory delegate) ou IServiceScopeFactory para resolver dentro de um scope correto.8Por que BackgroundService precisa criar scope a cada iteração?Dependency Injection
Porque o
abrir no tópico ↗ BackgroundService em si é Singleton, mas DbContext,
repositórios e MediatR handlers são Scoped. Sem um scope novo a cada iteração, você
(1) viola lifetime — exceção se ValidateScopes está on; ou pior, (2) reusa
o mesmo DbContext para sempre, acumulando ChangeTracker e estourando
memória, alem de leak de conexões. Scope por iteração reflete a "unit of work" — descarta
ao final e libera tudo.9Quando usar TryAdd* em vez de Add*?Dependency Injection
Em código de biblioteca / extension methods (
abrir no tópico ↗ AddMinhaLib()) onde você quer
registrar defaults sem sobrescrever escolhas do consumidor. O consumer chama
AddMinhaLib() primeiro e depois substitui peças específicas com AddSingleton.
Em código de aplicação (Program.cs), use Add* direto — você é dono do
composition root.10Service Locator vs DI - qual a diferenca?Dependency Injection
Ambos resolvem dependencias, mas DI (Constructor Injection) declara dependencias explicitamente no construtor,
enquanto Service Locator esconde dependencias resolvendo do container dentro dos metodos.
DI permite validacao em compile-time, facilita testes e nao acopla ao container.
Service Locator viola o principio de dependencias explicitas.
abrir no tópico ↗ 11Como funciona o disposal de scopes?Dependency Injection
Quando um
abrir no tópico ↗ IServiceScope e descartado (ex: fim de um HTTP request), o container chama Dispose() em todos os servicos
Scoped e IDisposable que foram resolvidos naquele scope.
Para Singleton, o disposal acontece quando o ServiceProvider raiz e descartado (shutdown da aplicacao).
Transient disposables nao sao tracked pelo container por padrao - cuidado com leaks.12Qual a diferenca entre um Design Pattern e um princípio SOLID?Design Patterns
Principios SOLID sao diretrizes gerais de design (ex: Dependency Inversion). Design Patterns sao solucoes concretas e nomeadas para problemas especificos (ex: Strategy implementa o principio Open/Closed). Patterns aplicam principios — nao sao a mesma coisa.
abrir no tópico ↗ 13Quando NAO usar um Design Pattern?Design Patterns
Quando a complexidade adicionada nao se justifica. Um pattern existe para resolver um problema recorrente. Se o problema e simples ou nao vai mudar, aplicar um pattern e over-engineering. Siga YAGNI (You Aren't Gonna Need It).
abrir no tópico ↗ 14Qual a diferenca entre Strategy e Template Method?Design Patterns
Strategy usa composicao — o algoritmo e injetado como dependencia. Template Method usa heranca — a classe base define o esqueleto e subclasses sobrescrevem steps. Strategy e mais flexivel e preferido com DI.
abrir no tópico ↗ 15Como escolher entre multiplas strategies em runtime?Design Patterns
Use um
abrir no tópico ↗ IEnumerable<IStrategy> injetado + um StrategyResolver (ou factory) que seleciona com base em criterios. Outra opcao: Keyed Services no .NET 8+ com [FromKeyedServices("key")].16Qual o risco de memory leak com events em C#?Design Patterns
O event mantem referencia ao subscriber via delegate. Se o subscriber tem lifecycle mais curto que o publisher e nao faz
abrir no tópico ↗ -=, o GC nao coleta o subscriber. Solucoes: WeakEventManager (WPF), IDisposable para unsubscribe, ou IObservable com Dispose().17Qual a diferenca entre Factory Method e Abstract Factory?Design Patterns
Factory Method cria um unico produto e usa heranca (subclasse decide). Abstract Factory cria uma familia de produtos relacionados e usa composicao (a factory e injetada). Na pratica, se voce so precisa de um tipo, use Factory Method. Se sao produtos que variam juntos (ex: UI components por plataforma), use Abstract Factory.
abrir no tópico ↗ 18Por que usar IHttpClientFactory em vez de new HttpClient()?Design Patterns
new HttpClient() nao reutiliza HttpMessageHandler, causando socket exhaustion. O factory gerencia o pool de handlers, aplica Polly/retry, e permite named/typed clients. E o Factory Method do ASP.NET Core para HTTP.19Qual a diferenca entre Decorator e Proxy?Design Patterns
Ambos implementam a mesma interface e delegam. A diferenca e a intencao: Decorator adiciona comportamento novo (logging, cache). Proxy controla acesso ao objeto real (lazy loading, auth check, remote call). Na pratica, a implementacao e quase identica.
abrir no tópico ↗ 20Como o middleware do ASP.NET Core e um Decorator/Chain?Design Patterns
Cada middleware recebe um
abrir no tópico ↗ RequestDelegate next e decide se chama next(context). E uma combinacao de Decorator (adiciona comportamento ao request) com Chain of Responsibility (pode interromper a cadeia). A ordem de registro define a ordem de execucao.21DbContext ja e Repository — por que criar outro?Design Patterns
E valido. O argumento a favor e encapsulamento: o Repository expoe apenas operacoes do dominio, nao todo o DbSet. Tambem facilita trocar EF por Dapper ou outro provider. O argumento contra e que e uma camada extra que duplica o que EF ja oferece.
abrir no tópico ↗ 22O que e Specification Pattern e como se relaciona com Repository?Design Patterns
Specification encapsula criterios de query como objetos. Em vez de
abrir no tópico ↗ GetActiveUsersByRole(role), voce passa new ActiveUsersSpec(role) ao Repository generico. Evita explosao de metodos no Repository e torna queries componiveis.23Finalizer vs Dispose?IDisposable
Dispose() e chamado pelo desenvolvedor (ou via
abrir no tópico ↗ using) — cleanup deterministico e imediato.
Finalizer (~Type) e chamado pelo GC como backstop — nao-deterministico, lento, promove o objeto para Gen 1+.
Na pratica: sempre chame Dispose(). Finalizer so existe como rede de seguranca caso alguem esqueca.
Em codigo moderno, prefira SafeHandle que ja tem finalizacao critica embutida.24Why does the dispose pattern call GC.SuppressFinalize?IDisposable
Quando
abrir no tópico ↗ Dispose() ja limpou tudo, nao ha razao para o GC chamar o finalizer.
GC.SuppressFinalize(this) remove o objeto da fila de finalizacao — evitando a promocao para Gen 1+ e a segunda coleta.
Resultado: cleanup mais rapido e menos pressao no GC.25using statement vs using declaration scope?IDisposable
using statement (
abrir no tópico ↗ using (...) {{ '{' }} {{ '}' }}) cria um escopo explicito — Dispose roda na chave de fechamento.
using declaration (using var x = ...;) estende o lifetime ate o fim do bloco pai (metodo, if, loop).
Escolha statement quando precisa liberar cedo (ex: mutex antes de continuar).
Escolha declaration para menos indentacao quando o recurso vive pelo metodo inteiro.
Ambos compilam para try/finally — Dispose roda mesmo com excecao.26throw vs throw ex?Error Handling
throw; preserva o stack trace original intacto — voce ve exatamente onde a exception originou. throw ex; reseta o stack trace para o ponto do rethrow, destruindo a informacao da origem. Sempre use throw; a menos que intencionalmente queira esconder a origem (raro). Em .NET 6+, ExceptionDispatchInfo.Throw(ex) e outra opcao que preserva tudo.27Quando capturar vs deixar exceptions propagarem?Error Handling
Capture apenas se voce pode fazer algo util: retry, wrap com contexto, traduzir para resposta HTTP, ou logar e re-lancar. Se nao sabe o que fazer, deixe propagar — um handler mais acima (middleware, global handler) vai tratar. Engolir exceptions (
abrir no tópico ↗ catch {{'{ }'}} vazio) e um dos piores antipatterns: esconde bugs e corrompe estado silenciosamente.28Exception filters — quando usar?Error Handling
Filters (
abrir no tópico ↗ catch (Exception ex) when (condition)) rodam antes do stack unwind completar — isso preserva o estado original para debugging. Use para: (1) branching por propriedades sem if aninhado, (2) logging centralizado sem duplicar, (3) distinguir erros transientes de permanentes. O filtro nao captura se retornar false — a exception continua procurando outro handler.29Por que nao usar try/catch para validacao de argumentos?Error Handling
Validacao de argumentos deve usar guard clauses com throw, nao try/catch. O caller esta passando dados invalidos — isso e um bug do caller, nao uma condicao excepcional que pode ser recuperada. Guard clauses fazem o metodo falhar imediatamente com uma mensagem clara, antes de qualquer efeito colateral. Try/catch e para quando o proprio metodo encontra uma falha durante execucao.
abrir no tópico ↗ 30Por que await Task.WhenAll so lanca a primeira exception?Error Handling
O
abrir no tópico ↗ await extrai a primeira exception do AggregateException interno da Task. Isso simplifica o caso comum (uma falha). Para ver todas as exceptions, guarde referencia das Tasks e inspecione task.Exception!.Flatten().InnerExceptions de cada Task faulted dentro do catch. Isso e importante quando voce dispara multiplas operacoes em paralelo e precisa saber quais falharam.31System.Text.Json vs Newtonsoft?Serialization
System.Text.Json e o default do .NET Core 3+. Vantagens: melhor performance (ate 2x mais rapido),
menor alocacao de memoria, integrado ao ASP.NET Core pipeline (sem dependencia extra), suporta
abrir no tópico ↗ Utf8JsonReader/Writer
de baixo nivel. Newtonsoft tem mais features legacy (LINQ-to-JSON com JObject, converters mais flexiveis,
[JsonProperty], reference handling nativo), mas e mais lento e requer pacote NuGet.
Para projetos novos, prefira System.Text.Json. Migre de Newtonsoft quando possivel.32Source generators for JSON?Serialization
A partir do .NET 6,
abrir no tópico ↗ [JsonSerializable] + JsonSerializerContext geram codigo de serialization
em compile-time. Beneficios: startup mais rapido (sem reflection), trim-friendly
(funciona com AOT/NativeAOT), e menor alocacao. Declare um partial class herdando de
JsonSerializerContext e decore com [JsonSerializable(typeof(MyType))].
O source generator cria os metadados em compile-time.33How to handle polymorphic serialization?Serialization
No System.Text.Json (.NET 7+), use
abrir no tópico ↗ [JsonPolymorphic] na base class com
TypeDiscriminatorPropertyName e [JsonDerivedType(typeof(Derived), "tag")]
para cada tipo derivado. O serializer inclui o discriminator ("$type":"credit") no JSON
e usa-o para resolver o tipo correto na deserializacao. Antes do .NET 7, era necessario escrever
um JsonConverter manual ou usar Newtonsoft com TypeNameHandling
(cuidado: TypeNameHandling.All e um vetor de ataque — nunca use em APIs publicas).34Por que SOLID e importante em projetos .NET Enterprise?SOLID Principles
Em projetos grandes, SOLID reduz o custo de mudanca. SRP significa que uma alteracao no calculo de imposto
nao afeta o envio de email. OCP permite adicionar novos tipos de pagamento sem tocar no processador existente.
DIP permite trocar o banco de dados de SQL Server para PostgreSQL sem alterar a camada de dominio.
Na pratica, SOLID e o que torna Clean Architecture e DDD possiveis.
abrir no tópico ↗ 35Como voce identifica violacoes de SRP no codigo?SOLID Principles
Sinais claros: a classe tem muitas dependencias injetadas (5+), o nome da classe usa "And" ou "Manager" generico,
metodos que nao se relacionam entre si, e a classe muda por motivos diferentes (ex: mudou o schema do banco E
mudou a regra de negocio na mesma classe). A regra pratica: se voce nao consegue descrever o que a classe faz
em uma frase sem usar "e", ela provavelmente viola SRP.
abrir no tópico ↗ 36Quais Design Patterns implementam OCP na pratica?SOLID Principles
Os principais sao: Strategy (troca de algoritmo via interface), Decorator (adiciona comportamento wrapping),
Template Method (esqueleto fixo com passos extensiveis), e Chain of Responsibility (pipeline de handlers).
No ASP.NET Core, o Middleware Pipeline e um exemplo perfeito de OCP: voce adiciona middlewares sem modificar os existentes.
O MediatR com behaviors (IPipelineBehavior) tambem segue OCP.
abrir no tópico ↗ 37Como LSP se aplica em cenarios reais com .NET?SOLID Principles
O caso mais comum e com
abrir no tópico ↗ IEnumerable<T> e ICollection<T>.
Se um metodo aceita ICollection<T> e voce passa ReadOnlyCollection<T>,
a chamada a Add() lanca NotSupportedException — violacao de LSP.
Outro cenario: repositorios. Se IRepository<T> tem Delete(), mas
AuditRepository lanca excecao porque nao permite delete fisico, isso viola LSP.
A solucao: segregar a interface (ISP ajuda aqui) ou usar composicao em vez de heranca.38Como ISP se relaciona com SRP?SOLID Principles
SRP e sobre classes, ISP e sobre interfaces — mas o espirito e o mesmo: coesao.
Se uma interface viola ISP, provavelmente a classe que a implementa viola SRP tambem.
No .NET, um bom exemplo:
abrir no tópico ↗ IHostedService tem apenas StartAsync e StopAsync.
O ASP.NET Core nao criou um IHostedServiceWithHealthCheckAndMetricsAndLogging.
Outro exemplo: o proprio IEnumerable<T> e segregado de ICollection<T>,
que e segregado de IList<T>. Cada nivel adiciona apenas o necessario.39Qual a diferenca entre Dependency Inversion (DIP) e Dependency Injection (DI)?SOLID Principles
DIP e um principio — diz que modulos de alto nivel devem depender de abstracoes,
nao de implementacoes concretas. DI e uma tecnica — o mecanismo pelo qual as dependencias
sao fornecidas (injetadas) em vez de criadas internamente. DI e a forma mais comum de implementar DIP.
Voce pode seguir DIP sem DI container (ex: passando a dependencia manualmente no construtor),
mas o container do ASP.NET Core (
abrir no tópico ↗ IServiceCollection) torna DI automatico e gerencia lifetimes
(Transient, Scoped, Singleton).40Se voce tivesse que escolher apenas 2 principios SOLID para aplicar em um projeto legado, quais escolheria?SOLID Principles
SRP e DIP. SRP porque e o mais impactante para reducao de complexidade — classes menores
e focadas sao mais faceis de entender, testar e refatorar. DIP porque habilita testabilidade imediata —
uma vez que voce inverte dependencias e injeta via interface, pode escrever testes unitarios sem depender
de infraestrutura (banco, APIs externas). Os outros principios naturalmente emergem quando voce aplica
SRP e DIP consistentemente. OCP vem do uso de interfaces (DIP). ISP vem de manter interfaces coesas (SRP).
LSP e garantido quando hierarquias sao simples e bem pensadas.
abrir no tópico ↗ ASP.NET Core37
1Authentication vs Authorization — explique a diferenca com exemploAuth & Authorization
Authentication e como mostrar sua identidade na portaria — voce prova quem e (token, credenciais). Authorization e a portaria verificar se voce tem acesso ao andar 5. No ASP.NET Core, Authentication popula
abrir no tópico ↗ HttpContext.User com claims. Authorization verifica essas claims contra policies. Um pode passar sem o outro: voce pode estar autenticado mas nao autorizado (403), ou nao autenticado (401).2JWT stateless — quais os pros e contras?Auth & Authorization
Pros: Servidor nao precisa de session state — qualquer instancia pode validar o token. Escala horizontalmente sem sticky sessions. Ideal para microservices. Contras: Nao da para revogar um token antes do exp (sem blacklist). Payload cresce com muitas claims. Se a signing key vazar, todos os tokens sao comprometidos. Refresh token resolve a revogacao — access token curto (5-15min) + refresh token longo no banco.
abrir no tópico ↗ 3Claims-based vs Role-based authorization?Auth & Authorization
Role-based e binario: usuario e Admin ou nao. Simples mas inflexivel — se voce precisa "Admin que trabalha no departamento X e tem mais de 2 anos", precisa criar roles cada vez mais especificos. Claims-based e granular: usuario tem claims Department=X, YearsOfService=3, Level=Admin. Policies combinam essas claims em regras flexiveis. Na pratica moderna, use claims + policies. Roles podem ser implementados como uma claim especial (ClaimTypes.Role).
abrir no tópico ↗ 4Quando usar Resource-Based authorization?Auth & Authorization
Quando a permissao depende do recurso, nao apenas do usuario. Exemplo classico: "usuario so pode editar seus proprios posts". Voce nao sabe se o usuario e dono do post ate carregar o post do banco. Entao voce carrega o recurso, passa para
abrir no tópico ↗ IAuthorizationService.AuthorizeAsync(user, post, "EditPost"), e o handler compara post.AuthorId == user.Id. Nao da para fazer isso com [Authorize] no controller porque o atributo roda antes do action — o recurso ainda nao existe.5Como revogar um JWT antes da expiracao?Auth & Authorization
JWT e stateless — por design, nao pode ser revogado. Solucoes: 1) Access token com vida curta (5-15 min) + refresh token no banco (revogavel). 2) Token blacklist em Redis — verifica jti a cada request (perde a vantagem stateless). 3) Token versioning — guarda uma versao no user profile; se a versao do token nao bate, rejeita. Na pratica, a combinacao access token curto + refresh token e a mais usada.
abrir no tópico ↗ 6BackgroundService vs IHostedService — quando usar cada um?Background Services
Use
abrir no tópico ↗ BackgroundService para trabalho continuo (loops, filas). Use IHostedService direto quando precisa de logica customizada em StartAsync/StopAsync (ex: abrir conexao, registrar em service discovery, warmup de cache). BackgroundService cobre 90% dos casos.7Como garantir graceful shutdown em background services?Background Services
1) Respeite o
abrir no tópico ↗ CancellationToken em todos os loops e awaits. 2) Configure HostOptions.ShutdownTimeout (padrao 30s). 3) Em StopAsync, aguarde a Task em andamento com timeout. 4) Use IHostApplicationLifetime.ApplicationStopping para drenar filas antes do stop. 5) Nunca ignore o token — use Task.Delay(interval, stoppingToken) em vez de Thread.Sleep.8Como lidar com excecoes em background services?Background Services
Envolva o corpo do loop em
abrir no tópico ↗ try/catch. Logue a excecao e continue o loop — nunca deixe a excecao propagar para fora do ExecuteAsync, pois isso para o servico. Para trabalho critico, combine com retry (Polly) e dead-letter queue. No .NET 8+, o padrao e StopHost — excecao nao tratada derruba a app inteira.9In-memory vs distributed cache — como escolher?Caching
In-memory e mais rapido (nanossegundos, sem rede), mas cada instancia tem sua copia — inconsistencia entre pods. Distributed (Redis) garante uma unica fonte de verdade para todas as instancias, mas adiciona latencia de rede (~1ms). Para dados que mudam pouco e sao lidos milhares de vezes (categorias, config), in-memory e ideal. Para dados de sessao ou inventario compartilhado, distributed.
abrir no tópico ↗ 10O que e cache stampede e como prevenir?Caching
Cache stampede (thundering herd) ocorre quando uma chave popular expira e centenas de requests simultaneos vao ao banco ao mesmo tempo para reconstruir o cache. Solucoes: 1) Locking com SemaphoreSlim — apenas um request reconstroi, outros esperam. 2) Probabilistic early expiration — renova o cache antes de expirar. 3) HybridCache (.NET 9) faz isso automaticamente. 4) Background refresh — um worker renova proativamente.
abrir no tópico ↗ 11Quais sao as estrategias de invalidacao de cache?Caching
As principais sao: 1) TTL — expiracao temporal, mais simples. 2) Explicit invalidation —
abrir no tópico ↗ cache.Remove() no write path. 3) Write-through — atualiza cache junto com o banco. 4) Event-driven — domain events invalidam via pub/sub (Redis, MediatR). 5) Versioned keys — muda a chave quando o dado muda (ex: products:v3). A escolha depende de quao stale os dados podem ficar e quao complexo e o grafo de dependencias.12Qual a diferenca entre hashing e encryption?Crypto & Auth
Hashing e uma funcao one-way: transforma dados em um digest de tamanho fixo (ex: SHA-256 gera 256 bits). Nao e possivel reverter. Usado para verificar integridade (checksums) e armazenar senhas (bcrypt). Encryption e two-way: com a chave correta, voce recupera o dado original. Symmetric (AES) usa uma chave para ambos; asymmetric (RSA) usa par de chaves. Usado para proteger dados em transito (TLS) e em repouso. Nunca use encryption para senhas — use hash com salt. Nunca use hash quando precisa recuperar o dado — use encryption.
abrir no tópico ↗ 13Por que NAO usar SHA-256 para senhas?Crypto & Auth
SHA-256 e uma funcao de hash rapida — projetada para calcular checksums de arquivos grandes eficientemente. Um atacante com GPU moderna calcula ~10 bilhoes de SHA-256 por segundo, fazendo brute force trivial. Para senhas, usamos algoritmos deliberadamente lentos: bcrypt (work factor ajustavel, ~0.3s por hash), Argon2 (memory-hard, resistente a GPU) ou PBKDF2 (iteracoes configuraveis, padrao NIST). Todos usam salt automaticamente, prevenindo rainbow tables. No ASP.NET Core Identity, o
abrir no tópico ↗ PasswordHasher usa PBKDF2 com 100k+ iteracoes e salt de 128 bits por padrao.14Quando usar symmetric vs asymmetric encryption?Crypto & Auth
Symmetric (AES): quando ambos os lados ja compartilham a chave de forma segura. Ideal para dados em volume — encriptar arquivos, banco de dados, comunicacao apos handshake TLS. E ~1000x mais rapido que RSA. Asymmetric (RSA/ECDSA): quando voce precisa trocar chaves com alguem que nunca encontrou (TLS handshake), assinar dados (JWT com RS256), ou verificar identidade sem compartilhar segredo. Na pratica, TLS usa asymmetric para trocar uma chave symmetric, e dai usa AES para os dados — melhor dos dois mundos. Regra: asymmetric para estabelecer confianca, symmetric para transferir dados.
abrir no tópico ↗ 15Por que usar RS256 ao inves de HS256 em microservicos?Crypto & Auth
Com HS256 (symmetric), a mesma chave que assina o token tambem verifica. Em microservices, isso significa que todo servico que precisa validar o JWT precisa ter a chave secreta — e qualquer um deles pode forjar tokens. Com RS256 (asymmetric), somente o auth server tem a chave privada para assinar. Os demais servicos usam a chave publica (via JWKS endpoint) para verificar — nao podem criar tokens falsos. Isso segue o principio do least privilege. Alem disso, rotacao de chaves e mais facil: publique a nova chave publica no JWKS, e os servicos atualizam automaticamente. ES256 (ECDSA) e uma alternativa moderna ao RS256 com chaves menores e performance melhor.
abrir no tópico ↗ 16Qual a diferenca entre OAuth 2.0 e OpenID Connect?Crypto & Auth
OAuth 2.0 e um framework de autorizacao: define como um app pode acessar recursos em nome do usuario (scopes). Ele NAO define quem e o usuario — o access token pode ser opaque. OpenID Connect (OIDC) e uma camada de identidade sobre OAuth 2.0: adiciona o ID Token (JWT com claims padrao como sub, email, name), o UserInfo endpoint, e o scope
abrir no tópico ↗ openid. Na pratica: OAuth 2.0 = "o app X pode acessar seus fotos". OIDC = "voce e joao@email.com e o app X pode acessar suas fotos". Sempre que precisar saber quem e o usuario, use OIDC. Quando so precisa acessar recursos, OAuth 2.0 basta.17Qual a diferença entre Polly v7 e Polly v8?HTTP Resilience
Polly v8 é uma reescrita completa. Usa
abrir no tópico ↗ ResiliencePipeline em vez de Policy.
Estratégias são compostas via builder pattern (new ResiliencePipelineBuilder()).
É thread-safe por design, suporta CancellationToken nativamente, e se integra
com Microsoft.Extensions.Http.Resilience para HttpClient.18Por que jitter é obrigatório?HTTP Resilience
Sem jitter, se 1000 clientes falharem ao mesmo tempo, todos vão retry em 2s, depois 4s, depois 8s
— criando picos sincronizados que sobrecarregam o serviço. Jitter randomiza os tempos,
distribuindo a carga uniformemente.
abrir no tópico ↗ 19Circuit breaker vs retry — como interagem?HTTP Resilience
Retry fica dentro do circuit breaker no pipeline. Se o circuit breaker abriu,
o retry nem executa — a chamada falha imediatamente. Isso evita que retries inúteis
sobrecarreguem um serviço já degradado. Ordem correta:
abrir no tópico ↗ Retry → CircuitBreaker → Timeout → HttpCall.20Bulkhead vs SemaphoreSlim — qual a diferença?HTTP Resilience
Conceitualmente são similares — ambos limitam concorrência. Mas o Bulkhead do Polly
integra com o pipeline de resilience: suporta queue, timeout na fila, métricas,
e se compõe com retry/circuit breaker.
abrir no tópico ↗ SemaphoreSlim é primitivo de sincronização
genérico sem essas integrações.21Middleware vs Filters — quando usar cada um?Middleware & Pipeline
Middleware roda para todo request HTTP, incluindo static files e health checks. Filters rodam apenas dentro do pipeline MVC/Razor, depois do routing. Use middleware para cross-cutting concerns globais (logging, CORS, exception handling). Use filters quando precisar de acesso ao ActionContext, model binding ou quiser granularidade por controller/action (authorization attributes, validation filters).
abrir no tópico ↗ 22How does middleware order affect behavior?Middleware & Pipeline
A ordem e critica. ExceptionHandler deve ser primeiro para capturar tudo. StaticFiles deve vir antes de Authentication para servir arquivos sem custo de auth. Routing deve vir antes de Authorization para que o framework saiba qual policy aplicar ao endpoint. Inverter Authentication e Authorization causa 500 em vez de 401/403. Inverter StaticFiles e Authentication forca auth para CSS/JS sem necessidade.
abrir no tópico ↗ 23Map vs MapWhen — qual a diferenca?Middleware & Pipeline
Map cria um branch no pipeline baseado no path prefix: app.Map("/api", apiBranch). Todos os requests que comecam com /api entram nesse branch separado. MapWhen e mais flexivel — aceita um predicado: app.MapWhen(ctx => ctx.Request.Headers.ContainsKey("X-Custom"), branch). Ambos criam um pipeline separado (branch), diferente de UseWhen que continua no pipeline principal se o branch nao fizer short-circuit.24O que acontece se eu registrar UseAuthorization antes de UseRouting?Middleware & Pipeline
O ASP.NET Core lanca
abrir no tópico ↗ InvalidOperationException em runtime: "EndpointRoutingMiddleware must be added before AuthorizationMiddleware". O framework detecta isso e falha fast. Authorization precisa saber qual endpoint foi matched para aplicar as policies corretas ([Authorize], [AllowAnonymous]). Sem routing, ele nao tem essa informacao.25Como testar middleware isoladamente?Middleware & Pipeline
Crie um
abrir no tópico ↗ DefaultHttpContext, configure o Request (path, headers, body), injete um RequestDelegate mock como next, e execute o middleware. Verifique o Response (status code, headers, body). Para middleware com DI, use WebApplicationFactory com TestServer para integration test — mais confiavel que mocking manual.26Qual a diferenca entre PUT e PATCH?REST API Design
PUT substitui o recurso inteiro — voce envia todos os campos. Se omitir um campo, ele volta ao valor default ou null. PATCH atualiza apenas os campos enviados — os demais permanecem inalterados. PUT e idempotente por definicao (mesma request = mesmo resultado). PATCH tecnicamente nao e idempotente (ex:
abrir no tópico ↗ {{ '{' }} "op": "increment", "path": "/views" {{ '}' }}). Na pratica, a maioria das APIs usa PATCH como se fosse idempotente (partial update simples).27Quando usar 401 vs 403?REST API Design
401 Unauthorized (nome confuso — deveria ser "Unauthenticated") significa que o request nao tem credenciais validas — token ausente, expirado ou invalido. O cliente deve se autenticar. 403 Forbidden significa que o servidor entendeu quem voce e (autenticado) mas voce nao tem permissao para esse recurso. Re-autenticar nao vai ajudar — voce precisa de permissoes diferentes. Exemplo: usuario comum tentando acessar
abrir no tópico ↗ /admin/users.28Por que usar [ApiController]?REST API Design
O atributo
abrir no tópico ↗ [ApiController] ativa 4 comportamentos automaticos: (1) Model validation — retorna 400 automaticamente se ModelState e invalido, eliminando checks manuais. (2) Binding source inference — tipos complexos vem de [FromBody], simples de [FromRoute/Query]. (3) Problem Details — erros seguem RFC 7807. (4) Attribute routing obrigatorio — garante URLs explicitas. Resultado: menos boilerplate, API mais padronizada e consistente.29Minimal APIs vs Controllers — quando usar cada?REST API Design
Minimal APIs: microservices com poucos endpoints, APIs de alta performance (menos overhead), prototipagem rapida, cloud functions. Controllers: APIs grandes com muitos endpoints (organizacao por classe), quando precisa de model validation automatica, quando usa filters extensivamente, equipes grandes onde convencao e importante. Na pratica, muitos projetos misturam: Minimal APIs para endpoints simples e Controllers para dominos complexos. A partir do .NET 7, Minimal APIs suportam filters e groups, fechando boa parte do gap.
abrir no tópico ↗ 30Qual a melhor estrategia de versionamento de API?REST API Design
Nao existe "melhor" universal. URL path (
abrir no tópico ↗ /v1/) e o mais usado na pratica (GitHub, Stripe, Google) por ser explicito e facil de cachear. Header e mais correto semanticamente (a URI do recurso nao muda). A recomendacao e: use URL path para APIs publicas (simplicidade) e header para APIs internas (flexibilidade). O importante e ser consistente e documentar bem. O pacote Asp.Versioning permite combinar estrategias com ApiVersionReader.Combine().31Qual a ordem de execucao dos filters?REST API Design
A ordem segue o pipeline: Authorization → Resource → Action → Exception → Result. Dentro de cada tipo, a ordem e: Global → Controller → Action (de fora para dentro). Na volta (depois da action), inverte: Action → Controller → Global (de dentro para fora). Exception filters so executam se houver exception nao tratada. E como uma boneca russa — cada escopo envolve o proximo.
abrir no tópico ↗ 32Resumo: o que um dev senior deve saber sobre REST APIs no .NET?REST API Design
Deve saber: (1) Principios REST e quando quebra-los pragmaticamente. (2) Status codes corretos — nao usar 200 para tudo. (3) Controllers vs Minimal APIs e trade-offs. (4) Versionamento — pelo menos URL path. (5) Problem Details (RFC 7807) para erros padronizados. (6) Filter pipeline e quando usar filter vs middleware. (7) Content negotiation e formatters. (8) Idempotencia e safety dos metodos HTTP. Na entrevista, demonstre que voce pensa em design, nao apenas em "funcionar".
abrir no tópico ↗ 33Cite 3 vulnerabilidades do OWASP Top 10 e como mitigar em .NETSecurity & OWASP
1. Injection (A03): EF Core parametriza queries automaticamente via LINQ. Para raw SQL, usar
2. Broken Access Control (A01): Usar
3. Security Misconfiguration (A05): Nunca expor stack traces em producao (
abrir no tópico ↗ FromSqlInterpolated que gera parametros. Nunca concatenar input em SQL.2. Broken Access Control (A01): Usar
[Authorize(Policy = "...")] com policies granulares. Para acesso a recursos, usar IAuthorizationService.AuthorizeAsync com resource-based authorization.3. Security Misconfiguration (A05): Nunca expor stack traces em producao (
app.UseExceptionHandler), adicionar security headers (X-Content-Type-Options, X-Frame-Options), desabilitar features desnecessarias, manter packages atualizados.34Como o EF Core previne SQL injection?Security & OWASP
O EF Core usa queries parametrizadas por padrao. Quando voce escreve LINQ (
abrir no tópico ↗ db.Users.Where(u => u.Name == input)), o EF traduz para SQL com parametros: WHERE Name = @p0. O valor do input nunca e concatenado na string SQL — ele e enviado separadamente como parametro. Mesmo FromSqlInterpolated converte a interpolacao C# em parametros SQL. A unica forma insegura e usar FromSqlRaw com concatenacao manual, que deve ser evitado.35O que e CORS e por que o browser bloqueia cross-origin requests?Security & OWASP
CORS e uma politica de seguranca implementada pelo browser (nao pelo servidor). Por padrao, o browser bloqueia requests JavaScript para origens diferentes da pagina atual — isso e a Same-Origin Policy. Existe para prevenir ataques onde um site malicioso faz requests a sua API usando cookies da vitima (CSRF). O servidor opt-in via headers CORS indicando quais origens sao confiáveis. Importante: CORS e apenas no browser — chamadas server-to-server (Postman, HttpClient) ignoram CORS completamente.
abrir no tópico ↗ 36Qual algoritmo de rate limiting voce usaria e por que?Security & OWASP
Token Bucket e o mais versatil para APIs. Ele permite bursts controlados (o bucket acumula tokens quando idle) mas mantem uma taxa media constante. Isso e perfeito para APIs onde usuarios fazem rajadas de requests (ex: carregar uma pagina) mas devem respeitar um limite medio. Para protecao de endpoints caros (relatorios, exports), uso Concurrency Limiter para limitar requests simultaneos. Para limites simples de plano (free: 100 req/min, pro: 1000 req/min), Fixed Window e o mais simples e previsivel.
abrir no tópico ↗ 37Como voce gerencia secrets em producao?Security & OWASP
Em producao, uso Azure Key Vault (ou AWS Secrets Manager). A aplicacao se conecta via Managed Identity (sem credenciais no codigo). O Key Vault e adicionado como configuration provider no startup:
abrir no tópico ↗ builder.Configuration.AddAzureKeyVault(uri, credential). Assim, secrets sao acessados via IConfiguration normalmente, sem mudanca de codigo. Para rotacao, o Key Vault suporta versionamento e a app pode recarregar configs periodicamente. Em desenvolvimento uso User Secrets, em CI/CD uso environment variables do pipeline. A regra de ouro: secrets nunca devem existir no repositorio.Messaging & Events17
1Como o Kafka difere de um message broker tradicional como RabbitMQ?Apache Kafka
Kafka e um log distribuido — mensagens sao persistidas em disco e nao sao removidas apos consumo.
Consumers controlam sua posicao (offset), permitindo replay.
RabbitMQ e um message broker que remove mensagens apos ack.
Kafka oferece throughput muito maior (milhoes/seg), mas RabbitMQ oferece routing mais flexivel (exchanges, bindings).
Kafka garante ordem por partition; RabbitMQ garante ordem por fila.
abrir no tópico ↗ 2Como o Kafka garante ordering de mensagens?Apache Kafka
Kafka garante ordem apenas dentro de uma partition.
Para garantir ordem de mensagens relacionadas, use a mesma partition key (ex: orderId).
Mensagens com a mesma key sempre vao para a mesma partition via hash consistente.
Ordem global (entre partitions) nao e garantida — se precisar disso, use um topic com apenas 1 partition (sacrificando paralelismo).
abrir no tópico ↗ 3O que acontece quando voce tem mais consumers do que partitions no mesmo group?Apache Kafka
Os consumers extras ficam idle (ociosos) — nao recebem nenhuma partition.
Isso porque cada partition so pode ser atribuida a um consumer por grupo.
Exemplo: 3 partitions e 5 consumers = 3 ativos + 2 ociosos.
Os ociosos servem como standby — se um consumer ativo cair, um ocioso assume na rebalance.
Regra pratica: numero de consumers <= numero de partitions.
abrir no tópico ↗ 4Explique a diferenca entre acks=1 e acks=all.Apache Kafka
Com acks=1, o leader confirma a escrita assim que persiste localmente,
sem esperar os followers. Se o leader cair antes da replicacao, a mensagem e perdida.
Com acks=all, o leader espera que todas as replicas in-sync (ISR) confirmem.
Isso garante que a mensagem sobrevive a falha de qualquer broker individual.
Trade-off: acks=all tem latencia maior, mas e obrigatorio para cenarios onde perda de dados e inaceitavel.
Combine com
abrir no tópico ↗ min.insync.replicas=2 para evitar que o cluster aceite escrita com apenas 1 replica.5Como voce garante que mensagens de um mesmo pedido cheguem na mesma partition?Apache Kafka
Use o orderId como partition key. O Kafka aplica hash na key para determinar a partition.
Mesma key = mesma partition = ordem de processamento garantida para aquele pedido.
Importante: nunca altere o numero de partitions apos definir — o hash muda e mensagens vao para partitions diferentes.
abrir no tópico ↗ 6Qual a diferenca entre auto-commit e manual commit? Quando usar cada um?Apache Kafka
Auto-commit: Kafka faz commit periodicamente (a cada
abrir no tópico ↗ auto.commit.interval.ms).
Simples, mas arriscado — se o consumer crash apos commit mas antes de processar, a mensagem e perdida (at-most-once).
Manual commit: voce faz commit apos processar com sucesso.
Garante at-least-once — se crash antes do commit, a mensagem sera reprocessada.
Use manual commit em producao. Combine com consumidor idempotente para lidar com duplicatas.7Sua empresa precisa processar pedidos. Kafka ou RabbitMQ? Justifique.Apache Kafka
Depende dos requisitos. Se preciso de audit trail, replay de eventos, ou multiplos consumers
independentes processando o mesmo stream (analytics, notificacao, faturamento), uso Kafka.
Se preciso de task queue simples com retry, dead-letter, e routing por atributos do pedido
(tipo, regiao, prioridade), uso RabbitMQ.
Na pratica, muitos sistemas usam ambos: Kafka como event backbone (OrderCreated, OrderShipped)
e RabbitMQ para commands especificos (ProcessPayment, SendEmail).
abrir no tópico ↗ 8Qual a diferenca entre comunicacao sincrona (HTTP) e assincrona (messaging)?Messaging Patterns
HTTP sincrono: o caller bloqueia esperando resposta. Acoplamento temporal — se o destino estiver fora, a chamada falha.
Messaging assincrono: o produtor envia e segue. O broker armazena a mensagem. O consumidor processa quando puder.
Vantagens do messaging: resiliencia (broker persiste), escalabilidade (competing consumers), peak shaving (fila absorve picos).
Desvantagens: complexidade operacional, eventual consistency, debugging mais dificil (tracing distribuido necessario).
abrir no tópico ↗ 9Quando usar Queue vs Topic?Messaging Patterns
Queue quando a mensagem deve ser processada por um unico handler — ex: processar pagamento, enviar email transacional, executar job em background.
Use competing consumers para escalar.
Topic quando multiplos servicos precisam reagir ao mesmo evento — ex: OrderCreated dispara email, inventario e analytics simultaneamente.
Regra pratica: se e um comando (faca X), use Queue. Se e um evento (X aconteceu), use Topic.
abrir no tópico ↗ 10Como garantir que todos os subscribers processem o evento mesmo se um falhar?Messaging Patterns
Cada subscriber tem sua propria fila no broker. Se o Consumer de email falhar, a mensagem fica na fila dele ate ser reprocessada (retry).
Os outros consumers (inventario, analytics) nao sao afetados — cada um tem fila independente.
Configure retry policies (exponential backoff) e Dead Letter Queue para mensagens que falham apos N tentativas.
Use Outbox Pattern no lado do publisher para garantir que o evento nunca se perca entre o banco e o broker.
abrir no tópico ↗ 11Quando escolher Request-Reply ao inves de HTTP direto?Messaging Patterns
Quando o consumidor pode estar temporariamente indisponivel e voce quer que a requisicao fique enfileirada ate ele voltar.
Quando voce quer load balancing nativo via competing consumers sem load balancer externo.
Quando o servico que responde nao expoe HTTP (ex: worker interno).
Evite Request-Reply para chamadas de baixa latencia onde HTTP direto e suficiente — o overhead do broker nao se justifica.
abrir no tópico ↗ 12Por que usar MassTransit ao inves de usar o client do RabbitMQ diretamente?Messaging Patterns
O client nativo (RabbitMQ.Client) e de baixo nivel: voce gerencia channels, serialization, retry, dead letter, routing manualmente.
MassTransit fornece: 1) Abstracao de broker — troque RabbitMQ por Azure Service Bus mudando uma linha.
2) Retry + circuit breaker embutidos. 3) Serializacao automatica.
4) Sagas com estado persistido. 5) Request-Reply tipado.
6) Observability (OpenTelemetry) nativo.
E como comparar usar ADO.NET puro vs Entity Framework — o ORM nao elimina o SQL, mas elimina o boilerplate e padroniza.
abrir no tópico ↗ 13Por que usar channels ao inves de multiplas connections?RabbitMQ
Cada Connection e uma conexao TCP completa com handshake AMQP (e possivelmente TLS). Isso consome file descriptors, memoria e tempo. Channels sao multiplexados na mesma Connection TCP — leves e rapidos de criar. O padrao recomendado e 1 Connection por aplicacao com 1 Channel por thread. Multiplas connections so fazem sentido para separar trafego de publish e consume em cenarios de alta carga.
abrir no tópico ↗ 14Qual exchange usar para enviar um evento que multiplos servicos precisam processar?RabbitMQ
Fanout e a escolha mais direta — broadcast para todas as queues vinculadas, sem filtro. Se os servicos precisam filtrar subconjuntos de eventos, use Topic com wildcards. Exemplo: InventoryService escuta
abrir no tópico ↗ order.#, PaymentService escuta order.payment.*. Fanout e mais simples e performatico quando todos os consumers querem todas as mensagens.15O que acontece quando uma mensagem e rejeitada (nacked) e existe uma DLQ configurada?RabbitMQ
Se o consumer faz
abrir no tópico ↗ BasicNack com requeue: false e a queue tem um x-dead-letter-exchange configurado, o broker roteia a mensagem para o Dead Letter Exchange (DLX). O DLX entao roteia para a DLQ via binding (usando a x-dead-letter-routing-key se configurada, ou a routing key original). Na DLQ, a mensagem fica disponivel para analise, reprocessamento manual ou consumo por um worker de retry. Se requeue: true, a mensagem volta para a queue original e a DLQ nao e acionada.16Como garantir que uma mensagem nao seja perdida entre o producer e o consumer?RabbitMQ
Tres pilares: (1) Publisher Confirms — o producer so considera a mensagem enviada apos o broker confirmar. (2) Persistent message + Durable queue — o broker persiste a mensagem em disco, sobrevive a restarts. (3) Manual Ack — o consumer so faz ack apos processar com sucesso; se crashar, o broker re-envia. Adicione Quorum Queues para resistir a falha de nos do cluster. E no consumer, idempotencia para lidar com duplicatas inevitaveis do at-least-once.
abrir no tópico ↗ 17MassTransit vs raw RabbitMQ client — trade-offs?RabbitMQ
MassTransit: produtividade alta — consumers tipados, retry/circuit breaker built-in, saga state machine, outbox, serialization automatica, troca de broker sem mudar codigo. Custo: abstracao adicional (mais dificil debugar problemas de roteamento), convencoes magicas (nomes de exchanges/queues gerados automaticamente), overhead de memoria. Raw client: controle total sobre exchanges, queues, routing keys, prefetch. Ideal quando voce precisa de topologias customizadas ou performance maxima. Regra: comece com MassTransit. So va pro raw client se tiver requisito especifico que justifique.
abrir no tópico ↗ Data & EF Core23
1Como o Change Tracker sabe quais colunas foram modificadas?Change Tracker Deep
No modo Snapshot (padrao): ao carregar a entidade, o EF Core copia todos os valores para um array interno (
abrir no tópico ↗ OriginalValues). Quando DetectChanges() roda, compara cada propriedade do objeto atual com o snapshot. So as propriedades com valores diferentes sao marcadas como IsModified = true, e o UPDATE gerado inclui apenas essas colunas. No modo Notification, a entidade dispara eventos ao mudar, eliminando a necessidade de comparacao.2DetectChanges() e O(n) — como otimizar com muitas entidades?Change Tracker Deep
1)
abrir no tópico ↗ AsNoTracking() para reads — entidades nao entram no tracker. 2) ChangeTracker.AutoDetectChangesEnabled = false — desabilita chamadas automaticas (voce chama manualmente quando precisar). 3) ChangeTracker.Clear() apos batches — limpa o tracker. 4) Use ExecuteUpdate/Delete (.NET 7+) para operacoes em massa sem carregar entidades. 5) Em cenarios extremos, use notification entities com INotifyPropertyChanged.3Se eu faco duas queries no mesmo DbContext, a segunda sobrescreve as mudancas locais?Change Tracker Deep
Nao. O Identity Map preserva a instancia existente. Se voce ja modificou
abrir no tópico ↗ product.Price = 99 e depois roda outra query que retorna o mesmo Product, o EF retorna a mesma instancia ja modificada. Os dados do banco sao descartados para essa entidade. Isso pode ser surpreendente — se voce precisa dos dados frescos do banco, use Entry(entity).ReloadAsync() que atualiza OriginalValues e CurrentValues.4Por que o DbContext nao e thread-safe?Change Tracker Deep
O Identity Map e as colecoes internas do Change Tracker nao usam locks. Se duas threads acessam o mesmo DbContext simultaneamente, podem corromper o estado interno: entries duplicadas, estados inconsistentes, exceptions durante SaveChanges. O padrao correto e um DbContext por unidade de trabalho (Scoped no ASP.NET = um por request). Para paralelismo, crie multiplos DbContexts via
abrir no tópico ↗ IDbContextFactory<T>.5O que e relationship fix-up?Change Tracker Deep
Quando o EF Core carrega uma entidade, ele verifica o Change Tracker por entidades relacionadas ja rastreadas e automaticamente preenche as navigation properties. Exemplo: se voce carrega um
abrir no tópico ↗ Order e o Customer ja esta rastreado (mesmo que tenha vindo de outra query), o EF seta order.Customer = customer automaticamente. Isso funciona nos dois sentidos — customer.Orders tambem e atualizado. O fix-up roda apos DetectChanges e apos materializacao de queries.6Concurrency exception — o que acontece com o Change Tracker apos a excecao?Change Tracker Deep
O Change Tracker mantem o estado anterior a tentativa de SaveChanges. As entidades ainda estao marcadas como Modified/Added/Deleted. Voce tem 3 opcoes: 1)
abrir no tópico ↗ ReloadAsync() em cada entry do ex.Entries (Database Wins), 2) Atualizar OriginalValues com GetDatabaseValuesAsync() para permitir novo SaveChanges (Client Wins), 3) ChangeTracker.Clear() e comecar do zero. Se nao fizer nada e tentar SaveChanges novamente, a mesma excecao ocorre.7Como garantir que operacoes financeiras nao percam updates?Change Tracker Deep
Para operacoes criticas: 1) Sempre use concurrency tokens (
abrir no tópico ↗ RowVersion). 2) Use pessimistic locking via SELECT ... FOR UPDATE com raw SQL quando conflitos sao frequentes. 3) Implemente idempotency keys para evitar duplicacao em retries. 4) Use domain events em vez de updates diretos — o evento carrega a intencao, nao o valor absoluto. Ex: DebitAccount(100) em vez de SetBalance(900). 5) Para operacoes de saldo, use ExecuteUpdateAsync com operacoes atomicas: SET Balance = Balance - @amount WHERE Balance >= @amount.8Update() vs Attach() + modificar propriedades — qual a diferenca?Change Tracker Deep
Update() marca todas as propriedades como Modified — o SQL gerado inclui SET para todas as colunas. Attach() marca como Unchanged, e quando voce modifica propriedades, so essas sao marcadas como Modified — SQL mais eficiente. O problema do Update(): pode causar conflitos de concorrencia desnecessarios e sobrescrever mudancas de outros campos feitas por outros requests. Prefira o padrao Load + Map: carregue com FindAsync, modifique so o necessario, salve.9Como o EF Core decide se uma entidade desconectada e nova ou existente?Change Tracker Deep
Pelo valor da chave primaria. Se PK tem o valor default do tipo (
abrir no tópico ↗ 0 para int, Guid.Empty para Guid), o EF assume que e nova (Added). Se tem valor nao-default, assume existente. Add() forca Added independente do PK. Attach() usa a logica de default. Update() forca Modified independente do PK. Cuidado com Guid: se voce gera o Guid no cliente, o EF acha que a entidade ja existe. Use Add() explicitamente nesse caso.10Como processar 100k registros sem estourar memoria?Change Tracker Deep
1) Use
abrir no tópico ↗ IDbContextFactory para criar DbContext por batch. 2) AsNoTracking() para leitura. 3) Processe em batches de 500-1000. 4) ChangeTracker.Clear() apos cada batch de escrita. 5) Para bulk insert, use SqlBulkCopy ou EFCore.BulkExtensions. 6) Para bulk update/delete, use ExecuteUpdateAsync/ExecuteDeleteAsync. 7) Considere streaming com AsAsyncEnumerable() para nao materializar tudo em memoria.11ExecuteUpdate bypassa interceptors e SaveChanges override. Como manter auditoria?Change Tracker Deep
Opcoes: 1) Inclua campos de auditoria diretamente no
abrir no tópico ↗ SetProperty: .SetProperty(p => p.UpdatedAt, DateTimeOffset.UtcNow). 2) Use database triggers para auditoria critica que nao pode ser bypassada. 3) Para soft delete em massa, use ExecuteUpdate setando IsDeleted = true em vez de ExecuteDelete. 4) Em cenarios hibridos, considere um audit log separado via SQL trigger ou CDC (Change Data Capture).12O que e o problema N+1 queries?EF Core Advanced
Acontece quando voce carrega uma colecao (1 query) e depois acessa uma navigation property de cada item (N queries). Exemplo: carregar 100 Orders e acessar
abrir no tópico ↗ order.Customer.Name de cada um gera 1 + 100 queries. Solucao: Include() para Eager loading, ou Select() para projecao. Em APIs, sempre use Include ou projete com Select.13Quando usar AsNoTracking?EF Core Advanced
Sempre que nao vai modificar os dados. Queries read-only (API GET, relatorios, dashboards). Elimina o snapshot do change tracker, reduz alocacoes e melhora throughput. Nao use quando precisa chamar
abrir no tópico ↗ SaveChanges depois — as mudancas nao serao detectadas. Em background services com muitas leituras, e obrigatorio para evitar memory leak.14Split queries vs single query — como escolher?EF Core Advanced
Single query (padrao): uma query com JOINs. Bom para 1-2 Includes simples. Split query: multiplas queries independentes. Bom quando tem multiplos Includes de colecoes (evita cartesian explosion). Trade-off: split queries fazem multiplos round-trips ao banco, entao em redes lentas ou com alta latencia, single query pode ser melhor. Meça sempre.
abrir no tópico ↗ 15EF Core ou Dapper — quando usar cada?EF Core Advanced
Use EF Core para operacoes CRUD padrao, domain models ricos, migrations e change tracking. Use Dapper para queries complexas (CTEs, window functions), relatorios read-only e hotspots de performance. Muitas equipes usam ambos: EF Core para writes e Dapper para reads complexos. Compartilhe a mesma connection string e use
abrir no tópico ↗ db.Database.GetDbConnection() para acessar a conexao do EF no Dapper.16Concorrencia otimista vs pessimista — quando usar cada?EF Core Advanced
Otimista (padrao do EF Core): nao faz locks, verifica no write. Bom para a maioria dos cenarios web — conflitos sao raros. Pessimista (SELECT ... FOR UPDATE): lock explcito no read. Use quando conflitos sao frequentes e o custo de retry e alto (ex: operacoes financeiras). No EF Core, pessimista requer SQL raw ou
abrir no tópico ↗ ExecuteUpdate com hints.17Como implementar soft delete com EF Core?EF Core Advanced
1) Adicione
abrir no tópico ↗ IsDeleted e DeletedAt na entidade. 2) Configure Global Filter: builder.HasQueryFilter(e => !e.IsDeleted). 3) Override SaveChanges para interceptar EntityState.Deleted e converter para Modified com IsDeleted = true. 4) Para queries admin que precisam ver deletados, use IgnoreQueryFilters(). Cuidado: o filtro tambem se aplica a Include — entidades deletadas em relacoes tambem sao filtradas.18Qual o lifetime correto do DbContext?EF Core Basics
DbContext deve ser Scoped — um por request HTTP. Ele nao e thread-safe. AddDbContext ja registra como Scoped por padrao. Em background services (Singleton), use IDbContextFactory ou IServiceScopeFactory para criar instancias por operacao. Nunca use Singleton — o change tracker acumula entidades e consome memoria.19Code-First vs Database-First — quando usar cada?EF Core Basics
Code-First para projetos novos — voce define classes C# e o EF gera migrations. Controle total, versionado em Git. Database-First quando ja existe um banco (legado) — use
abrir no tópico ↗ dotnet ef dbcontext scaffold para gerar classes. Muitas equipes usam um hibrido: scaffold inicial + Code-First dali em diante.20Por que Add() e sincrono se AddAsync() existe?EF Core Basics
Add() apenas muda o estado da entidade no Change Tracker — e uma operacao 100% em memoria. AddAsync() existe para o caso raro de value generators que precisam ir ao banco (como HiLo sequences que reservam blocos de IDs). Com estrategias comuns como Identity (auto-increment) ou Guid.NewGuid(), AddAsync internamente roda de forma sincrona e retorna Task.CompletedTask. Usar AddAsync sem necessidade cria overhead da state machine async sem ganho.21Update() marca todas as propriedades como Modified. Qual a alternativa?EF Core Basics
Carregue a entidade com
abrir no tópico ↗ FindAsync, modifique as propriedades diretamente (product.Price = 99.99m), e chame SaveChangesAsync. O Change Tracker detecta automaticamente quais colunas mudaram e gera um UPDATE so com essas colunas. Update() marca tudo como Modified, gerando um UPDATE com todas as colunas — mais lento e pode causar conflitos de concorrencia desnecessarios.22Lazy vs Eager vs Explicit loading — quando usar cada?EF Core Basics
Eager loading (
abrir no tópico ↗ .Include()): carrega tudo em uma query com JOIN. Use quando sabe que vai precisar dos dados relacionados. Explicit loading (.Entry().Collection().LoadAsync()): carrega sob demanda, mas explicitamente. Use quando so precisa em certos caminhos. Lazy loading: carrega automaticamente ao acessar a navigation property. Perigoso — causa N+1 queries silenciosamente. Evite em APIs. Prefira Eager com Include + ThenInclude.23Por que evitar Lazy Loading em APIs?EF Core Basics
Lazy loading carrega dados ao acessar navigation properties. Em uma API, ao serializar para JSON, o serializer acessa todas as properties — disparando queries para cada relacao. Um
abrir no tópico ↗ Order com 10 OrderItems que tem Product gera 1 + 10 + 10 queries (N+1). Use Include para Eager loading ou projete com Select para carregar so o necessario.Testing & DevOps17
1Por que nao usar Stopwatch para benchmarking?Benchmarking
Stopwatch mede wall-clock time de uma unica execucao. A primeira execucao inclui JIT compilation,
que pode ser 10-100x mais lenta. Nao elimina outliers causados por GC, context switches ou background processes.
Nao mede alocacoes de memoria nem pressao no GC. Nao faz analise estatistica — voce nao sabe se a diferenca entre
duas abordagens e real ou ruido. BenchmarkDotNet resolve tudo isso: warmup, iteracoes multiplas, estatistica,
diagnosticos de memoria e relatorios comparativos. E a diferenca entre "acho que e mais rapido" e "comprovadamente e 3.2x mais rapido com 95% de confianca".2O que BenchmarkDotNet mede que timing manual nao mede?Benchmarking
Alem de tempo preciso (com warmup e eliminacao de JIT overhead), mede: alocacoes de memoria (bytes por operacao),
GC collections por geracao (Gen0/Gen1/Gen2), desvio padrao para saber se o resultado e estavel,
intervalo de confianca para saber a margem de erro, e ratios automaticos para comparar abordagens.
Tambem suporta disassembly (mostra o assembly/IL gerado), hardware counters, e exportacao para CSV/JSON/Markdown.
abrir no tópico ↗ 3Qual o impacto de alocacoes de memoria no GC?Benchmarking
Cada alocacao no managed heap eventualmente precisa ser coletada pelo GC. Objetos de vida curta (Gen0) sao baratos
de coletar, mas em volume alto causam colecoes frequentes. Objetos que sobrevivem para Gen1/Gen2 sao mais caros
porque o GC precisa compactar memoria e pausar threads (stop-the-world em Gen2 sem concurrent GC).
Em hot paths (loops internos, request handlers), reduzir alocacoes com
abrir no tópico ↗ Span<T>,
stackalloc, ArrayPool e string.Create pode reduzir latencia
de P99 significativamente. BenchmarkDotNet com [MemoryDiagnoser] e a ferramenta para medir isso.4Como interpretar o Ratio no BenchmarkDotNet?Benchmarking
O Ratio compara cada benchmark com o
abrir no tópico ↗ Baseline. Ratio 1.00 = igual ao baseline.
Ratio 0.50 = 2x mais rapido. Ratio 2.00 = 2x mais lento. O RatioSD mostra
a variacao — se RatioSD e grande relativo ao Ratio, a diferenca pode nao ser significativa.
Exemplo: Ratio 0.98 com RatioSD 0.05 = praticamente igual. Ratio 0.30 com RatioSD 0.01 = 3.3x mais rapido, resultado confiavel.5Qual a diferenca entre container e VM?Docker & Deployment
Um container compartilha o kernel do host e isola apenas o processo via
namespaces e cgroups. Uma VM virtualiza o hardware completo com um kernel proprio (guest OS).
Containers sao mais leves (MBs vs GBs), iniciam em segundos, e permitem maior
densidade. VMs oferecem isolamento mais forte pois tem kernel separado.
Para microservicos .NET, containers sao a escolha padrao. VMs sao preferidas quando
se precisa de isolamento total (multi-tenancy, compliance).
abrir no tópico ↗ 6Por que usar multi-stage build?Docker & Deployment
Multi-stage build separa o ambiente de build do runtime.
O primeiro stage usa a imagem SDK (~900MB) para compilar e publicar.
O segundo stage copia apenas os binarios publicados para uma imagem de runtime (~220MB).
Isso resulta em imagens menores (sem SDK, codigo fonte, ou ferramentas),
mais seguras (menor superficie de ataque), e com
build mais rapido (melhor aproveitamento de cache de layers).
abrir no tópico ↗ 7Como services se comunicam no Docker Compose?Docker & Deployment
O Compose cria uma rede bridge automaticamente. Cada service e registrado
como hostname DNS na rede. A API acessa o banco por
abrir no tópico ↗ db:5432 e o Redis por
redis:6379 — usando o nome do service como hostname.
Nao e necessario expor portas entre services; apenas portas que precisam ser acessadas
do host (ex: 5000:8080 para a API) sao mapeadas.8Qual a diferenca entre liveness e readiness probe?Docker & Deployment
Liveness verifica se a aplicacao esta viva (nao travou, nao entrou em deadlock).
Se falhar, o Kubernetes reinicia o container.
Readiness verifica se a aplicacao pode receber trafego (banco conectado, cache pronto).
Se falhar, o Kubernetes remove do load balancer mas nao reinicia.
Liveness deve ser leve (ex: retornar 200). Readiness pode verificar dependencias externas.
Nunca coloque checks pesados no liveness — se o banco cair, nao faz sentido reiniciar a app.
abrir no tópico ↗ 9Quando usar Blue-Green vs Canary deploy?Docker & Deployment
Blue-Green e ideal quando voce quer um switch completo e instantaneo com rollback
rapido. Requer o dobro de infraestrutura. Bom para aplicacoes com estado.
Canary e ideal quando voce quer validar a nova versao com trafego real antes
de promover. Menor risco pois poucos usuarios sao afetados. Ideal para
microservicos stateless com metricas de observabilidade.
abrir no tópico ↗ 10Qual a diferenca entre unit test e integration test?Testing & TDD
Unit test isola uma unica unidade (classe/metodo) — todas as dependencias sao substituidas por mocks/stubs.
Roda em milissegundos, sem I/O real. Integration test exercita multiplos componentes colaborando —
pode usar banco real (Testcontainers/in-memory), HTTP real (WebApplicationFactory) ou filesystem.
A fronteira e: se o teste depende de infraestrutura externa, e integration. Se depende apenas de logica in-process, e unit.
abrir no tópico ↗ 11O que e o padrao Arrange-Act-Assert (AAA)?Testing & TDD
E a estrutura padrao de um teste unitario: Arrange — configure o cenario (crie objetos, configure mocks);
Act — execute a acao sendo testada (chame o metodo); Assert — verifique o resultado esperado.
Separar claramente essas fases torna o teste legivel e facil de manter. Cada teste deve ter exatamente um Act
e assertions relacionadas ao mesmo comportamento.
abrir no tópico ↗ 12Convencoes de nomeacao de testes?Testing & TDD
A convencao mais comum em .NET e
abrir no tópico ↗ MethodName_Scenario_ExpectedBehavior.
Exemplo: Withdraw_InsufficientFunds_ThrowsInvalidOperationException.
Outra abordagem e Should_ExpectedBehavior_When_Condition.
O importante e que o nome do teste descreva completamente o cenario sem precisar ler o corpo.
Use nomes longos e descritivos — testes nao sao chamados em codigo de producao.13Quando usar mock vs dependencia real?Testing & TDD
Use mock quando: a dependencia e lenta (HTTP, DB), nao-deterministica (clock, random),
ou voce precisa verificar interacoes (foi chamado? com quais parametros?).
Use real quando: e um value object puro, uma colecao in-memory, ou em integration tests
onde voce quer testar a colaboracao real entre componentes. A regra: mock na fronteira do sistema
(I/O, infra), real para logica de dominio.
abrir no tópico ↗ 14Por que xUnit cria uma nova instancia da classe para cada teste?Testing & TDD
Para garantir isolamento total entre testes. Cada
abrir no tópico ↗ [Fact] roda com um objeto novo —
nenhum estado residual de um teste anterior pode interferir. Isso elimina a classe inteira de bugs causados por
shared mutable state entre testes. E por isso que xUnit nao precisa de [SetUp]/[TearDown] —
o construtor e o Dispose fazem o mesmo papel, de forma mais idiomatica em C#.15Quando usar Moq vs NSubstitute?Testing & TDD
Ambos resolvem o mesmo problema. Moq tem ecossistema maior, mais exemplos online e suporta
abrir no tópico ↗ Strict mode.
NSubstitute tem API mais limpa e menos verbosa — o codigo fica mais legivel.
Em projetos novos, NSubstitute e geralmente preferido pela simplicidade. Em projetos existentes, mantenha consistencia.
O mais importante nao e a ferramenta, e saber o que mockar e por que.16Por que usar Testcontainers ao inves de InMemory database?Testing & TDD
InMemoryDatabase do EF Core nao suporta transactions, constraints, stored procedures, nem SQL especifico do provider.
Testes passam com InMemory mas falham em producao. Testcontainers sobe o banco real (PostgreSQL, SQL Server)
em um container Docker — voce testa contra o mesmo motor que roda em producao. E mais lento, mas muito mais confiavel.
Use IClassFixture para reutilizar o container entre testes e Respawn para limpar dados.17O que e mutation testing e por que coverage nao basta?Testing & TDD
Code coverage mede se uma linha foi executada, mas nao se foi verificada.
Voce pode ter 100% coverage com zero assertions. Mutation testing (Stryker.NET) modifica o codigo de producao
e verifica se testes detectam a mudanca. Se uma mutacao sobrevive, significa que seus testes nao verificam
aquele comportamento. O mutation score e uma metrica muito mais confiavel que line coverage.
abrir no tópico ↗ Cloud & AWS31
1Qual a diferenca entre Deployment e StatefulSet?EKS & Kubernetes
Deployment trata Pods como gado: nomes aleatorios, rolling update paralelo, qualquer Pod pode receber o trafego. StatefulSet da identidade estavel — db-0, db-1, db-2 — e bota up/down em ordem (one at a time), com um PersistentVolumeClaim por Pod. Use Deployment quando o servico e stateless e qualquer instancia serve qualquer request. Use StatefulSet quando ha estado persistente vinculado a uma identidade (replica de banco, broker, lider de cluster).2Quando NAO usar Fargate?EKS & Kubernetes
Quando voce precisa de DaemonSets (Fargate nao roda), de GPU/instance storage, ou tem trafego sustentado de alto throughput (Fargate cobra premium por vCPU/memoria que voce pagaria mais barato em EC2 reservado). Fargate brilha em cargas variaveis, isolamento por compliance, ou quando manter nodes patcheados ja virou problema operacional.
abrir no tópico ↗ 3Qual a diferenca entre Service type=LoadBalancer e Ingress?EKS & Kubernetes
Service type=LoadBalancer cria um LB por servico — caro se voce tem 20 microservicos (20 ALBs). Ingress com um Ingress Controller (AWS LB Controller) compartilha um unico ALB entre varios servicos via host/path-based routing. Use LoadBalancer para TCP/UDP/non-HTTP, ou quando precisa de IP estatico dedicado. Use Ingress para HTTP(S) — e o padrao economico e idiomatico.4Por que IRSA e melhor que guardar AccessKey num Secret?EKS & Kubernetes
Tres motivos. (1) Sem keys de longa duracao: o Pod recebe credenciais STS temporarias (1h por default), que rotam sozinhas — nada para vazar, nada para revogar manualmente. (2) Escopo por Pod: cada ServiceAccount aponta para uma role com least privilege, em vez de uma IAM user gigante compartilhada entre todos os Pods. (3) Auditoria limpa: CloudTrail rastreia a role assumida e o token OIDC ate o ServiceAccount/Pod, em vez de mostrar "iam-user-app" sem contexto. Secret com AccessKey hardcoded e o jeito errado de fazer isso na AWS desde 2019.
abrir no tópico ↗ 5Por que dependencia externa nao pode ir em liveness probe?EKS & Kubernetes
Liveness em falha = restart do container. Se voce checa o banco em liveness e o banco cai por 30 segundos, o kubelet mata todas as replicas, elas reiniciam, encontram o banco ainda fora, falham liveness de novo, e entram em
abrir no tópico ↗ CrashLoopBackOff. Quando o banco volta, voce ainda precisa esperar todas as replicas saudaveis subirem e passarem startup — voce tornou um incidente externo em um incidente cascateado seu. A regra: liveness so checa "estou em deadlock?" (ex: contador de threads ativos, capacidade de fazer um SELECT 1 no proprio processo). Dependencias externas vao em readiness — Pod sai do load balancer enquanto o problema dura, sem matar nada.6Quando usar startup probe alem de liveness/readiness?EKS & Kubernetes
Quando a aplicacao demora a subir (cold cache, migrations, warm-up JIT). Sem startup probe, voce precisa setar
abrir no tópico ↗ initialDelaySeconds alto no liveness — mas dai liveness fica "frouxo" para sempre, demora pra detectar deadlock real. Com startup probe, voce da um budget grande para boot (failureThreshold * periodSeconds = ex 5 minutos), e so depois liveness/readiness comecam a rodar com thresholds agressivos. App que sobe em 90s sem startup probe vai entrar em loop se voce setou liveness com initialDelaySeconds=30.7Por que sempre configurar DLQ + alarm em SQS de producao?SQS / SNS / EventBridge
Sem DLQ, mensagens "poison" (que falham todo processamento) ficam em loop eterno na queue principal: bloqueiam, consomem retry budget, podem mascarar incidentes reais. Com DLQ +
abrir no tópico ↗ maxReceiveCount, SQS move automaticamente apos N falhas — voce alarme em quantidade na DLQ, investiga offline, e a queue principal segue limpa. Adicione tambem MessageRetentionPeriod alto na DLQ (14 dias) para ter tempo de diagnosticar.8SNS direto para Lambda vs SNS → SQS → Lambda?SQS / SNS / EventBridge
SNS → Lambda e mais simples (1 hop a menos), e bom para notificacoes em que perda eventual e aceitavel. Mas voce perde: (1) buffer se Lambda esta com erros ou capacity, (2) retry budget granular (Lambda assincrono tem 2 retries fixos), (3) DLQ por consumer (Lambda async DLQ existe mas e por funcao, nao por evento). Para eventos de dominio em prod, prefira SNS → SQS → Lambda: paga ~$0.40/M extra de SQS, ganha durabilidade + observabilidade.
abrir no tópico ↗ 9Quando usar SNS vs EventBridge?SQS / SNS / EventBridge
Use SNS quando o fan-out e simples (N subscribers iguais), o volume e alto, e a filtragem cabe em atributos (set de chave-valor fixo). Use EventBridge quando precisa de roteamento baseado no payload (ex: "todos pedidos com total > 1000 reais"), tem integracao com SaaS externos via partner event sources, ou precisa de schema registry e archive/replay. Regra pratica: comeca com SNS; sobe para EventBridge quando filtros viram complexos ou precisa de archive.
abrir no tópico ↗ 10Quando usar Kinesis em vez de SQS?SQS / SNS / EventBridge
Tres sinais: (1) sequencia importa e voce precisa de ordem por entidade — Kinesis garante ordem por shard via PartitionKey, SQS so com FIFO e gargalo de 300 msg/s. (2) Replay faz parte do design — se um consumer da pau, voce quer reprocessar 6h de eventos sem republicar; SQS deleta apos consumir. (3) Multiplos consumers leem o mesmo stream com posicao independente — SQS so atende 1 consumer pool por queue. Custo: Kinesis paga por shard-hora mesmo ocioso, entao trafego muito baixo ou esporadico fica caro vs SQS.
abrir no tópico ↗ 11Por que precisamos de idempotência em consumers SQS?SQS / SNS / EventBridge
Porque SQS garante at-least-once, não exactly-once. Cenários onde a mesma mensagem chega 2x: (1) o consumer processa mas crasha antes de fazer DELETE da mensagem; (2) o visibility timeout expira durante processamento — SQS reentrega; (3) rebalance entre consumers; (4) redrive de DLQ horas depois. Sem idempotência, side effects executam-se múltiplas vezes — duplo email, duplo charge, duplo write. Idempotência é obrigatória, não opcional, em qualquer broker at-least-once.
abrir no tópico ↗ 12Diferença entre idempotência natural e idempotência forçada?SQS / SNS / EventBridge
Natural — a operação em si não tem efeito ao ser repetida (INSERT RDF triple, PUT por ID conhecido, DELETE, UPSERT). Forçada — usa-se um dedup store (Redis SET NX, SQS FIFO MessageDeduplicationId, DynamoDB conditional) para descartar duplicados antes de processar. Prefira natural quando possível — menos código, menos infra, menos chance de bug. Forçada quando o side effect não é naturalmente seguro de repetir (envio de email, charge no gateway de pagamento). Sistemas maduros combinam os dois.
abrir no tópico ↗ 13Como dimensionar o TTL do dedup key no Redis?SQS / SNS / EventBridge
O TTL deve ser maior que a maior janela de retry possível do broker. SQS pode reentregar dentro do
abrir no tópico ↗ visibilityTimeout × maxReceiveCount — tipicamente minutos a horas. Para mensagens que vão pra DLQ e são redirected manualmente depois, considere ainda mais. 24h é default seguro para a maioria dos casos. Se TTL é demasiado curto, retry tardio passa pelo dedup. Se é demasiado longo, Redis acumula chaves indefinidamente (custo de memória). Heurística: TTL = visibilityTimeout × maxReceiveCount × 2 com piso de 1 hora.14Como garantir idempotencia num consumer SQS at-least-once?SQS / SNS / EventBridge
O pattern canonico e conditional write em DynamoDB: antes de processar, tenta inserir um registro com hash key =
abrir no tópico ↗ MessageId e ConditionExpression: attribute_not_exists(id). Se ja existe, retorna 200 sem fazer nada (idempotent skip). Se inserir com sucesso, processa, e marca status=processed no mesmo registro. Vantagens: persistente, atomico, escalavel, e o TTL do DDB limpa registros antigos. Alternativas mais leves: Redis com SET NX (rapido mas nao durable), ou desenhar a operacao em si idempotente (UPSERT em vez de INSERT, status transitions com WHERE no current state).15SQS FIFO MessageDeduplicationId resolve idempotencia?SQS / SNS / EventBridge
Resolve parcialmente e por 5 minutos. SQS FIFO faz dedup automatico de mensagens com o mesmo
abrir no tópico ↗ MessageDeduplicationId dentro da janela de 5min — otimo para retries imediatos do publisher (a rede caiu, voce tentou de novo, SQS percebe). Mas nao protege contra: redelivery por visibility timeout expirado, redrive de DLQ horas depois, ou bugs no consumer. Sempre combine com idempotencia no consumer (conditional write em DDB). SQS FIFO dedup e camada complementar, nao substituicao.16Por que logs estruturados (JSON) sao melhores que texto plano em CloudWatch?CloudWatch & X-Ray
CloudWatch Logs Insights indexa campos de JSON automaticamente — voce escreve
abrir no tópico ↗ fields userId, latencyMs | filter latencyMs > 1000 direto. Em texto plano voce precisa de parse @message "User * failed login in *ms" as user, ms que e fragil (formato muda, regex falha) e lento (regex em toda linha). JSON tambem propaga contexto estruturado de outras camadas (correlation IDs, trace IDs) que voce pode filtrar/joinar entre servicos. Em microservicos com 50 servicos, isso e a diferenca entre conseguir investigar e nao.17Por que nunca alarmar em media de latencia?CloudWatch & X-Ray
Latencia tem distribuicao long-tail: 99% dos requests podem responder em 50ms e 1% em 5s — a media fica em ~100ms e parece OK, mas voce tem 1% de usuarios com experiencia horrivel. Sempre alarme em percentis (p95, p99).
abrir no tópico ↗ p99 = 5000ms diz "1% pior caso esta em 5s"; e disso que o usuario reclama. Tambem facilita SLO: o target "99% dos requests em < 500ms" mapeia direto em p99 latency < 500ms.18Como X-Ray correlaciona traces entre servicos?CloudWatch & X-Ray
Atraves de propagation: cada request HTTP carrega um header
abrir no tópico ↗ X-Amzn-Trace-Id (formato X-Ray) ou traceparent (W3C, padrao OTel). Quando o servico A chama o servico B, ele copia o trace ID para o request — B continua o mesmo trace, gerando um novo span filho. Para isso funcionar end-to-end, todos os servicos precisam do mesmo propagator (ou um Collector configurado para traduzir entre formatos). Em .NET com OpenTelemetry e AWSXRayPropagator, isso e automatico: HttpClient instrumentado injeta o header, e o middleware da app proxima extrai.19Como diagnosticar um endpoint lento em producao em menos de 5 minutos?CloudWatch & X-Ray
Caminho com observabilidade boa: (1) abre CloudWatch dashboard, ve
abrir no tópico ↗ p99 latency by endpoint — confirma qual rota subiu. (2) Vai em X-Ray Service Map, filtra por esse endpoint na ultima hora, ordena por response time — ve traces dos requests lentos. (3) Abre um trace exemplo: o waterfall mostra que 80% do tempo foi em um span chamado db:Cards.GetByOwner — gargalo localizado no banco, nao na app. (4) Volta para Logs Insights filtrando traceId = X e ve o SQL emitido — query sem indice em OwnerId. Sem essa stack (logs estruturados + traces correlacionados + metrics em percentis), voce gasta horas em kubectl logs e grep.20Vale a pena adotar OpenTelemetry em .NET ou continuar com SDK proprietario?CloudWatch & X-Ray
OpenTelemetry virou o padrao de fato em .NET 8 — a Microsoft adotou as APIs
abrir no tópico ↗ ActivitySource/Meter no proprio framework, e qualquer biblioteca moderna (HttpClient, EF Core, ASP.NET Core) ja emite OTel automaticamente. Vantagens: codigo de dominio nao acopla a vendor; troca de Datadog para New Relic ou X-Ray e config de Collector, nao reescrita de instrumentation. Custo: leve curva de aprendizado de Collector config + um sidecar/daemonset a mais. Para time em < 3 microservicos com SDK ja funcionando, talvez nao valha. Para time em 20+ servicos ou negociando contrato APM, vale 100%.21Como tratar um engenheiro que causou um incidente serio?Blameless Postmortem
A reuniao de postmortem trata do incidente, nao da pessoa. Voce agradece a transparencia (especialmente se a pessoa se reportou); investiga por que o sistema permitiu o erro (faltou guardrail, faltou review, faltou alerta cedo); e converte action items em mudanca do sistema. Conversas de performance/desempenho da pessoa, se cabiveis, sao em outro espaco, com manager — nunca na reuniao de incidente. Misturar as duas coisas mata a cultura. Excecao: dishonestidade ou negligencia repetida (mesmo padrao varias vezes apos action items claros) — ai vira topico de performance, fora do postmortem.
abrir no tópico ↗ 22Qual a diferenca entre causa imediata e causa raiz?Blameless Postmortem
A causa imediata e o que disparou o incidente agora — "o deploy reduziu connection pool". A causa raiz e o problema sistemico que permitiu que a causa imediata fosse possivel — "nao temos validacao tipada de config no CI". Consertar so a causa imediata (re-treinar a pessoa, reverter o commit) deixa o sistema igualmente fragil para a proxima vez que alguem repetir o erro. Consertar a causa raiz (adicionar guardrail estrutural) elimina a categoria de incidente. Postmortem util sempre chega na causa raiz; 5 Whys e a tecnica mais barata pra forcar isso.
abrir no tópico ↗ 23Como decidir se uma degradacao parcial e P1 ou P2?Blameless Postmortem
Tres dimensoes: (1) escopo — % de usuarios ou % de revenue afetado; (2) funcionalidade — checkout/pagamento e P1, busca de catalogo lentinha provavelmente P2; (3) trend — se esta piorando, promove ja para nao perder tempo de resposta. Regra de oncall: na duvida, sobe. E mais barato chamar gente que nao precisava do que descobrir tarde demais. Voce ajusta para baixo depois sem custo.
abrir no tópico ↗ 24Conta-me um incidente em producao que viveste — focada no que mudou depoisBlameless Postmortem
Pista do que entrevistador quer ouvir: (1) sintoma — o que o usuario sentiu, com numero. (2) processo de deteccao — como soubemos, em quanto tempo. (3) diagnostico — como reduzimos o espaco de busca rapidamente (dashboards, traces, logs). (4) mitigacao imediata — rollback, feature flag, escala. (5) causa raiz — sistemica, nao a pessoa que deployou. (6) O que mudou no sistema depois — a parte mais importante. Resposta ruim termina em "fui mais cuidadoso depois". Resposta boa termina em "adicionamos validacao tipada de config no CI, agora essa categoria inteira de bug e impossivel". Se voce nao consegue articular "o que mudou", o incidente foi desperdiçado.
abrir no tópico ↗ 25O que voce faz quando recebe um alerta sem runbook as 3h da manha?Blameless Postmortem
Trabalho de oncall na hora: investigar pelo que da pra inferir do nome do alerta + servico, e mitigar (rollback, restart, feature flag, escalar) ate o sintoma sumir. Apos resolver, action item obrigatorio: criar o runbook antes de voltar pra cama (se possivel) ou em primeira hora do dia seguinte. Alerta sem runbook que ja acordou alguem nao deveria estar vivo na proxima rotacao — ou ganha runbook, ou deleta. A coisa que define um time maduro nao e nao receber alertas; e nao receber duas vezes o mesmo alerta sem runbook.
abrir no tópico ↗ 26Quantos action items um bom postmortem produz?Blameless Postmortem
Geralmente 3 a 7. Menos que 3, suspeito que nao foi a fundo (qual outra categoria de incidente isso poderia causar?). Mais de 10, suspeito que misturou "wishlist" com "preciso fazer agora". Cada item deve ser concreto, ter owner, ter prazo, e mudar algo no sistema (codigo, config, processo, alerta). Items de "documentar" ou "comunicar" sao bem-vindos mas nao substituem mudancas estruturais. O sucesso real do postmortem se mede 6 meses depois: a categoria de incidente repetiu? Se nao, foi util.
abrir no tópico ↗ 27Diferenca entre SLO e SLA em uma frase?SLI / SLO / Error Budget
SLO e o target interno que o time persegue (ex: 99.9% das requests em < 500ms); SLA e o contrato externo com cliente que tem consequencia financeira se violado (sempre mais frouxo que SLO, por margem de seguranca). SLI e a metrica real que voce mede para validar SLO/SLA.
abrir no tópico ↗ 28Se voce esta com 80% do error budget gasto e a equipe quer fazer um release de feature grande, voce aprova?SLI / SLO / Error Budget
Depende de quanto falta da janela. Se sao 80% gasto no dia 5 de um SLO mensal, e sinal de que a janela vai estourar — segura o release, foca no incidente atual. Se sao 80% no dia 25, voce esta no caminho de aterrissar OK; o release pode passar mas com canary mais lento e rollback mais sensivel. Error budget nao e um semaforo binario; e uma metric continua que ajusta o risco aceitavel do team. A pergunta certa nao e "passou de 50%?" mas "qual o burn rate atual e quanto resta da janela?".
abrir no tópico ↗ 29Por que nunca usar media para medir latencia?SLI / SLO / Error Budget
Latencia tem distribuicao long-tail — alguns requests sao naturalmente mais lentos (cache miss, hot partition, GC pause). A media e dominada pela maioria rapida e esconde o 1% que vive um inferno. P99 = 5s significa que 1 em 100 usuarios espera 5 segundos — em throughput de 1k req/s, sao 10 usuarios por segundo com experiencia ruim. SLO e error budget sempre se expressam em percentis, nao em media. Bonus: medias respondem mal a outliers, percentis sao estaveis.
abrir no tópico ↗ 30Quando alarmar em CPU faz sentido?SLI / SLO / Error Budget
Quase nunca como sinal primario. CPU alta nao e diretamente um sintoma que o usuario sente — o usuario sente latencia, erros, ou tela travada. CPU 95% pode estar saudavel (utilizacao alta = uso eficiente) ou patologico (queue de requests crescendo). Alarme em sintomas (latency p99, error rate, queue depth) e use CPU como causa a investigar depois. Excecao: CPU como early-warning combinado com saturation — "CPU > 80% E queue depth subindo" e bom sinal de "vai estourar logo, escala antes".
abrir no tópico ↗ 31Como voce define o numero do SLO — por que 99.9% e nao 99.99%?SLI / SLO / Error Budget
Tres perguntas que respondem: (1) O que o usuario tolera? Para checkout de e-commerce, 99.95% e sufficient (15 min/mes de downtime). Para um marcapasso, 99.9999%. (2) O que o concorrente entrega? Seu SLO precisa ser pelo menos paridade. (3) O que voce consegue operar? Cada 9 extra multiplica custo: multi-region, replicacao sincrona, failover automatico. Calcule o custo de 1 nove a mais e veja se o ROI de negocio cobre. Geralmente o "sweet spot" para SaaS B2B e 99.9% (43 min/mes) — caro o suficiente para ser bom, barato o suficiente para ser sustentavel.
abrir no tópico ↗ Architecture Leadership36
1Por que escrever um ADR em vez de só decidir e mover?ADRs
Três razões. (1) Memória organizacional: 6-12 meses depois, ninguém lembra o porquê — sem ADR alguém vai "consertar" o que era decisão intencional. (2) Onboarding: novo dev lê 15 ADRs e entende como o sistema chegou onde chegou — vale dias de pareamento. (3) Pensamento forçado: o ato de listar opções e consequências obriga você a pensar em alternativas que você não tinha visto. ADRs bons são curtos (1-2 páginas), não burocráticos.
abrir no tópico ↗ 2Como você convence a liderança a soltar o controle e adotar Advice Process?ADRs
Mostre os custos reais do modelo atual: lead time de decisão (dias → semanas), número de decisões "informalmente" tomadas sem registro (porque o processo é caro), turnover dos seniors que viraram approvers em tempo integral. Proponha piloto em um time stream-aligned com domínio bem definido. Mostre métricas comparativas em 3 meses (DORA, número de ADRs registrados, satisfação). A diferença é dramatica e quantitativa — não é argumento ideológico.
abrir no tópico ↗ 3Quantos ADRs um sistema "saudável" tem?ADRs
Não tem número certo, mas em sistemas que vi maduros: ~20-50 ADRs ao longo de 2-3 anos de vida — tipicamente 1-2 por mês quando a equipe está engajada. Menos que isso geralmente significa: (a) decisões importantes não estão sendo registradas, (b) time pequeno demais (todas decisões viraram folclore), ou (c) muito recente. Mais que 100 por ano sugere que entidades menores estão virando ADR — vire convenção/linter pra elas e libere o canal pra decisões realmente arquiteturais.
abrir no tópico ↗ 4Quando NÃO usar um agente, e usar workflow determinístico?AI Agents em Produção
Se o problema tem solução determinística clara, não use LLM. LLM é caro, não-determinístico, e introduz novos modos de falha. Use agente quando: (a) o input é livre/ambíguo (linguagem natural, conversa); (b) o caminho de execução varia por contexto; (c) tradução semântica é necessária (não regex). Para fluxos estruturados (workflow BPMN, ETL), use orquestrador tradicional — vai ser mais barato e confiável. Anti-pattern comum: LLM substituindo um
abrir no tópico ↗ if.5Por que RAG falha mesmo com embeddings bons?AI Agents em Produção
Várias razões. (1) Query do usuário é vaga ou abstrata; embedding não captura intenção. (2) Chunking ruim — corta no meio de uma seção relevante. (3) Documento certo tá top-30, não top-5 (precisa re-ranking). (4) Falta de contexto — chunk isolado sem o título da seção pai. (5) Modelo recupera bem mas ignora o contexto no prompt. Diagnóstico: log o que foi recuperado, e teste se a presença daquele texto no contexto fazia o modelo acertar. Se sim, problema é retrieval; se não, problema é prompt/modelo.
abrir no tópico ↗ 6Por que evals são mais difíceis que testes unitários tradicionais?AI Agents em Produção
Três motivos: (1) Não-determinismo — mesma entrada pode produzir saída diferente; precisa rodar N vezes e medir distribuição. (2) Múltiplas respostas válidas — não tem "uma resposta certa"; precisa medir qualidade subjetiva. (3) Saída livre — não cabe em assertion; precisa heurística ou outro modelo julgando. Resultado: evals levam mais tempo pra construir e rodar; precisam de eval set de centenas de exemplos pra ter estatística confiável. Investimento, não opcional.
abrir no tópico ↗ 7Você não pode prevenir prompt injection 100%. Como lidar?AI Agents em Produção
Aceitar o limite e desenhar pra falha. Nunca dê a um agente uma tool que cause dano irreversível sem confirmação humana — independente de quão bom o guardrail. Use múltiplas camadas: detector de input + system prompt defensivo + permission check antes de tool + audit log. Se prompt injection for um vetor crítico (e.g., agente lendo emails de terceiros), reduza superfície: minimize quais tools ele pode usar, limite o que pode acessar, segregue por contexto. Defesa em profundidade, não defesa por perímetro.
abrir no tópico ↗ 8Se você tivesse 3 meses para construir um agente em produção do zero, em que ordem investiria?AI Agents em Produção
Mês 1: caso de uso + eval set. Identifique 1 fluxo concreto onde o agente entrega valor mensurável. Construa eval set de 50-100 casos. Sem isso, não há iteração possível. Mês 2: MVP harness mínimo — single agent, tools claras, system prompt boa, output schema validado, logging básico. Rode em piloto interno. Mês 3: guardrails e produção — input/output guardrails, observability completa, cost limits, A/B em fração de tráfego, on-call setup. Lançamento progressivo (1% → 10% → 100%). O que NÃO faria nos 3 meses: multi-agent, fine-tuning, RAG sofisticado. Esses vêm depois quando o problema for claro.
abrir no tópico ↗ 9Por que MTTR sem CFR é enganador?DORA & Accelerate
MTTR sozinho pode parecer ótimo (15min!) mas se CFR é 30%, você está restaurando rápido um sistema que quebra direto — péssimo. MTTR sozinho também incentiva esconder incidentes ("foi só uma instabilidade"). As métricas só fazem sentido combinadas: alta DF + alta LT (velocidade), com baixa CFR + baixa MTTR (estabilidade). Olhar uma sem as outras vira gaming.
abrir no tópico ↗ 10Time deploya 1x por mês mas tem CFR baixo e MTTR baixo — é elite?DORA & Accelerate
Não — é low na velocidade e isso tem consequências sérias. Deploys mensais geralmente são gigantes (2-4 semanas de trabalho), o que aumenta blast radius por mudança, dificulta diagnosticar regressão, e desincentiva entregar coisas pequenas (porque "não vale a pena pra esse deploy"). O CFR baixo aqui é geralmente porque cada deploy tem 2 semanas de QA manual, que custa caro e atrasa feedback de produto. Velocidade e estabilidade tendem a andar juntas — se você está só em uma das duas, está deixando dinheiro/qualidade na mesa.
abrir no tópico ↗ 11Como você mediria cultura generativa sem virar pseudociência?DORA & Accelerate
Sinais comportamentais concretos: (1) % de postmortems com action item de mudança sistêmica (não "treinar a pessoa"); (2) tempo entre incidente detectado e reportado ao time amplo (em cultura patológica esconde-se); (3) número de ADRs propostos por engenheiros não-seniors (em cultura generativa, qualquer um propõe); (4) Westrum survey simples (6 perguntas, anual). Combine sinais — nenhum sozinho é definitivo, mas a tendência conjunta é robusta.
abrir no tópico ↗ 12Como você apresenta DORA pra liderança sem virar pressão tóxica?DORA & Accelerate
Três regras: (1) Mostre tendência, não snapshot — tendência conta a história. (2) Conecte a outcome de negócio — "lead time caiu 40%, e nossa hipótese de feature X foi validada em 1 semana em vez de 1 mês". (3) Foque na capability, não na métrica — "queremos investir em pipeline para reduzir lead time, o que vai liberar X tempo dos times". Sem essa ponte, virou cobrança numérica e o time joga o jogo. Com essa ponte, vira ferramenta de investimento informado.
abrir no tópico ↗ 13Por que o trabalho de engenharia em sistemas com LLM é majoritariamente harness?Harness Engineering
O modelo é commodity intercambiável — Claude, GPT, Gemini fazem 80% das mesmas tarefas com qualidade similar. O diferencial vem do harness: como você dá contexto certo, como valida saída, como detecta regressão, como controla custo, como integra com o restante do sistema. Trocar modelo é uma linha de config; reescrever harness é projeto de meses. Investir em harness é investir no que dura.
abrir no tópico ↗ 14Qual o trade-off entre AGENTS.md gigante e AGENTS.md curto?Harness Engineering
Gigante (10k+ tokens): consome janela de contexto que poderia ir pra código relevante; modelo "se perde" e ignora partes; cada chamada custa mais. Curto demais: agente não tem contexto suficiente, alucina convenções. O sweet spot: AGENTS.md topo (3-5k tokens) com o essencial; sub-docs (CONTRIBUTING.md, arquitetura) só carregadas quando necessárias via retrieval. Pense em AGENTS.md como onboarding de 30 minutos pra dev novo — não documento exaustivo.
abrir no tópico ↗ 15Como você decide se um sensor inferencial (LLM-as-judge) vale o custo?Harness Engineering
Pergunta de ROI. (1) Pode ser feito por sensor computacional? (Linter, schema, teste estrutural.) Se sim, use. (2) Se precisa semântica, o erro detectado é caro? Se sim (vai virar incidente em prod, ou ofende o usuário), vale. (3) Você tem gold-set pra calibrar? Sem isso, LLM-as-judge tem accuracy baixa e dá falsos positivos que vão queimar a confiança do time. Resumo: prefira computacional; reserve inferencial para falhas semânticas de alto impacto, com calibração.
abrir no tópico ↗ 16Vale construir todo esse harness para um pequeno time / projeto?Harness Engineering
Não desde o dia 1. Comece com o que já é gratuito — linter, type checker, AGENTS.md, schema. Vai longe. Adicione sensors caros (mutation testing, LLM-as-judge) quando você sente o gap específico: "outputs regrediram silenciosamente", "testes passam mas bug em prod". Cada sensor tem custo de manutenção; não pague custo por proteção que não está te ajudando. Use o princípio sistêmico: identifique o gargalo real, invista lá. Veja também systems thinking.
abrir no tópico ↗ 17Por que platform team é uma evolução de "DevOps engineer"?Platform Engineering
DevOps engineer original era pessoa híbrida que ajudava cada time individualmente. Não escala — pra 10 times você precisa de 10 DevOps. Platform team produtiza o trabalho: em vez de fazer pra cada time, constrói self-service que qualquer time usa sem ajuda humana. O DevOps engineer continua importante no stream-aligned team (devops culture); o platform team é quem provê as ferramentas pra essa cultura escalar. Veja Team Topologies para o desenho organizacional.
abrir no tópico ↗ 18Como você começaria a construir uma plataforma do zero?Platform Engineering
Não comece pelo Backstage portal. Comece identificando o caminho mais repetido — geralmente "criar API REST" ou "criar worker". Faça um template Git com chassis pronto, CI funcionando, observability default. Documente. Depois adicione 1-2 capabilities self-service (CLI ou GitHub Action) pra criar database, fila etc. Só depois de 3-4 capabilities estabilizadas, pense em portal unificado. Plataforma boa cresce por demanda — não por especulação de "vou precisar disso".
abrir no tópico ↗ 19Como você evita que o chassis vire god-object?Platform Engineering
Quebra em módulos opcionais:
abrir no tópico ↗ chassis.observability, chassis.auth, chassis.idempotency. Cada service só importa o que precisa. O método AddChassis() compõe defaults razoáveis mas permite opt-out. Outra regra: nada no chassis pode ser obrigatório a ponto de impedir que o service exista sem ele. Se precisa, é candidato a sair do chassis e virar service mesh / sidecar (Envoy/Istio).20Como você prioriza o que entra na plataforma?Platform Engineering
Por frequência × dor. Fale com os stream-aligned teams: o que vocês fazem 3+ vezes por mês que é doloroso? Coisas frequentes e dolorosas viram capability de plataforma; coisas frequentes mas baratas ficam como template; coisas raras ficam fora. Anti-pattern: priorizar pelo "vai ser legal" ou "queremos a tech X" — terminam em plataforma sem usuários. Roadmap dirigido por demanda real, com NPS interno medido, é como você comprova que a plataforma vale o investimento.
abrir no tópico ↗ 21Como você mede se a plataforma está funcionando?Platform Engineering
Quatro métricas combinadas: (1) DORA dos stream-aligned — lead time, deploy frequency, MTTR, change failure rate (ver página). Devem melhorar à medida que plataforma cresce. (2) NPS interno — pergunte aos times trimestralmente: "recomendaria a plataforma a outro time?". (3) Time-to-first-deploy — quanto tempo um service novo leva da decisão até produção. Meta: < 1 dia. (4) Cost de operação por time — % do tempo dos stream-aligned gasto em toil (deveria cair). Se 3 dessas 4 estão melhorando, plataforma vale o investimento.
abrir no tópico ↗ 22Por que mapear subdomínios antes de microservices?Strategic DDD
Microservice mal cortado é dívida arquitetural. Se você fatia em service-por-tabela, vai gastar o dobro coordenando deploys e queries distribuídas, sem ganho de autonomia. Subdomínio define a granularidade natural do negócio — um service por subdomínio (ou por bounded context dentro do subdomínio) tende a se manter coeso. Sem essa lente, o sistema vira "distributed monolith".
abrir no tópico ↗ 23Como você decide o tamanho de um bounded context?Strategic DDD
Grande o suficiente para resolver um problema de negócio coeso, pequeno o suficiente para caber em um time. Se dois times brigam pelo mesmo modelo dentro do BC, está grande demais — divida na linha do conflito. Se um time precisa coordenar com três outros pra fazer qualquer mudança, está pequeno demais — junte. O sinal definitivo é a linguagem: dentro do BC, "Cliente" deve ter um único significado claro.
abrir no tópico ↗ 24E quando o domain expert usa um termo ambíguo?Strategic DDD
Você força a desambiguação na conversa. "Quando você fala 'cancelar', você quer dizer estornar (devolve o dinheiro) ou só não enviar (mantém cobrança)?" — essa pergunta sozinha já resolve metade dos bugs futuros. O ubiquitous language não é descobrir o que está na cabeça do expert; é negociar uma linguagem comum. Às vezes o expert também não tinha clareza.
abrir no tópico ↗ 25Quando faz sentido ACL e quando é overkill?Strategic DDD
ACL vale quando o upstream tem um modelo diferente do seu e você vai usar muito tempo. Vale a tradução. Exemplos: integração com legado, sistema externo, vendor que pode mudar. Overkill quando: é só um endpoint chamado uma vez, ou o modelo upstream é praticamente igual ao seu (use Conformist). Heurística: se um DTO direto já serve, é Conformist. Se você está repensando o domínio do upstream pra encaixar no seu, é ACL.
abrir no tópico ↗ 26Como você convenceria a liderança a investir em redesenhar bounded contexts?Strategic DDD
Em métricas, não em estética. Mostre: (1) lead time por feature subindo (DORA); (2) % de features que precisam coordenar N times; (3) número de incidentes em produção que atravessaram N services; (4) atrito de onboarding de novo dev (semanas até primeiro PR útil). Tudo isso piora linearmente com BCs mal cortados. Proponha uma migração incremental via Strangler Fig — não big bang. A liderança aprova o que tem ROI mensurável.
abrir no tópico ↗ 27Por que algumas iniciativas de "reduzir bug backlog" falham mesmo com time grande?Systems Thinking
Porque atacam o stock (corrigir bugs existentes) sem alterar o inflow (taxa em que novos bugs entram). Em 2 meses o time aniquilou 200 bugs, mas 220 novos entraram. O backlog "voltou". A solução sistêmica é mexer no inflow: testes de regressão automáticos, code review com critério, observability que pega bugs antes de virarem ticket. O outflow (corrigir bugs) ainda importa — mas sem mexer no inflow, é trabalho infinito.
abrir no tópico ↗ 28Por que adicionar pessoas a um projeto atrasado pode atrasar mais (lei de Brooks)?Systems Thinking
Loops sistêmicos: (1) novo dev consome tempo de quem já estava (training, code review) — outflow cai temporariamente; (2) coordenação cresce O(N²) — mais reuniões, mais merge conflicts; (3) ramp-up tem delay de meses — você paga o custo agora e colhe ganho depois. Resultado: nos primeiros 3 meses, o stock de trabalho restante cai mais lento que antes. Sistemicamente, você precisa contratar antes do projeto ficar crítico, não durante. Brooks viu isso em 1975; segue verdade.
abrir no tópico ↗ 29Liderança pede pra "reduzir tempo de resolução de incidentes em 50%". Como você responde?Systems Thinking
Pergunto qual é a teoria de mudança. Reduzir só pelo parâmetro (mais oncall, escalada mais rápida) é #12 — vai dar 10-15% e exausta o time. Reduzir via informação (alarme proativo, dashboards melhores) é #6 — vai dar 30-50% e é sustentável. Reduzir via regra ("você build, você run") é #5 — vai mexer no inflow de incidentes ao longo de 1 ano. Apresento as três alternativas com custo/tempo, e pergunto qual nível de transformação eles querem. O importante é não aceitar "50%" como número e correr atrás sem refletir.
abrir no tópico ↗ 30Qual desses arquétipos você já viu na vida real?Systems Thinking
Quase todos. Cito um exemplo: fixes that fail — time aumenta timeout HTTP de 30s pra 60s porque chamadas estavam estourando. Bug "resolvido". 3 meses depois, request lenta segura thread, pool esgota, sistema cai. A causa real era query N+1 não diagnosticada. Tratar sintoma escondeu o problema até virar incidente serio. A lição que aprendi: toda vez que você "afrouxa um limite", investigue por que estava sendo violado. O sintoma é informação.
abrir no tópico ↗ 31Dê um exemplo de como você usou systems thinking pra resolver problema de engenharia.Systems Thinking
Histórico real: time gastava 40% de capacidade em incidentes. Análise sistêmica revelou loop reforçador — incidentes geravam horas extras → engenheiros cansados → mais bugs → mais incidentes. Atacar o stock (corrigir bugs faster) não iria virar. Identifiquei ponto de alavanca em #6 (informação): observability terrível, ninguém via incidente até cliente reclamar. Investimos 1 trimestre em SLOs, burn rate alerts, dashboards. CFR caiu 50% em 6 meses. O loop reforçador inverteu: menos incidentes → time descansado → menos bugs → menos incidentes. Stock (qualidade) finalmente subiu sustentavelmente.
abrir no tópico ↗ 32Como você decide quando criar um platform team?Team Topologies
Quando você tem 3+ stream-aligned teams resolvendo o mesmo problema técnico (cada um seu próprio Kubernetes, seu próprio CI/CD, seu próprio observability stack). A duplicação custa mais que o investimento em um platform team. Antes desse ponto, é YAGNI — você está montando estrutura cara para um problema que ainda não tem.
abrir no tópico ↗ 33Por que limitar Collaboration?Team Topologies
Collaboration tem alto custo de coordenação (reuniões, alinhamentos, decisões compartilhadas). Para problemas novos, esse custo vale: você reduz risco de retrabalho. Para problemas conhecidos, é desperdício — duas pessoas resolvendo onde uma só basta. A regra: collaboration enquanto a interface não está clara; assim que ficou clara, vire X-as-a-Service e ganhe escala. Times que ficam em Collaboration eternamente terminam fundidos (porque a coordenação já era 80% do trabalho) ou em conflito.
abrir no tópico ↗ 34Como você decide se um time está sobrecarregado?Team Topologies
Métricas técnicas só (velocity, deploy frequency) escondem o problema. Olhe (1) tempo de onboarding subindo, (2) % de oncall com page real fora de horário, (3) features prometidas e adiadas, (4) turnover acima do baseline. Se 2+ desses estão piorando, o time está sobrecarregado — e a solução geralmente não é "contratar mais": é dividir o escopo (criar outro time stream-aligned cobrindo metade do domínio), ou reduzir carga extrínseca (investir em plataforma).
abrir no tópico ↗ 35Como você aplicaria inverse Conway maneuver na prática?Team Topologies
Sequência: (1) Defina os bounded contexts e subdomínios que você quer; (2) Desenhe times stream-aligned por BC; (3) Identifique as platform capabilities que vão atendê-los; (4) Mova pessoas para os novos times antes de mexer no código; (5) Use Strangler Fig para migrar — não big bang. Sem o passo 4, todo mundo continua falando com os mesmos colegas e o sistema continua refletindo o organograma antigo.
abrir no tópico ↗ 36Por que Component teams não funcionam para entregar valor rápido?Team Topologies
Cada feature de negócio precisa de várias camadas (frontend, API, banco, infra). Se cada camada tem um time, a feature vira projeto de coordenação entre 4 times — handoffs, esperas, fila de prioridades de cada time. Lead time vira semanas para algo que um time stream-aligned faria em dias. Component teams escalam camadas técnicas; o que precisa escalar é fluxo de valor. Por isso a transição típica é: component → stream-aligned + platform para o que era componente realmente caro de replicar.
abrir no tópico ↗ Semantic Web & Knowledge Graphs27
1Por que registrar IGraph como Singleton em vez de Scoped?dotnetrdf-csharp
Depende do uso. Para um grafo carregado uma vez no startup (TBox, ontologia estática), Singleton é correto — thread-safe pela natureza de IGraph (operações de leitura são seguras). Para grafos com escrita concorrente, considere
abrir no tópico ↗ ThreadSafeGraph de dotNetRDF, ou Scoped por request. Para triplestore remoto via SparqlQueryClient, registrar com HttpClient via IHttpClientFactory — Singleton do client, HttpClient é reutilizado.2Quando IGraph in-memory chega no limite?dotnetrdf-csharp
Heurística: até ~1-5 milhões de triplas funciona bem em servidor com 8-16 GB RAM. Acima disso, custo de GC e startup ficam dolorosos. Soluções: (1) TripleStore com persistência em disco (dotNetRDF tem providers para SQLite, embedded); (2) Triplestore externo (Fuseki/Neptune) acessado via SparqlQueryClient; (3) VDS.RDF.Storage abstrai backend (você troca implementação sem mudar código de query). Para TBox estática (ontologia), in-memory é sempre OK. Para ABox crescente, planejar storage externo desde o início.
abrir no tópico ↗ 3Vale a pena mapear JSON para RDF "on the fly" em cada request?dotnetrdf-csharp
Depende do tamanho e do uso. Se você consulta um catálogo: vale materializar uma vez (cron, evento de mudança) e queries SPARQL hit no grafo persistido. Se é fluxo transitório (resposta de webhook que enriquece com ontologia): mapping inline é OK. Custo: cada CreateUriNode/Assert tem overhead. Para alta vazão, considere pipeline batched: acumula triplas em buffer e flush a cada N. Mesma lógica que insert em lote vs row-by-row em SQL.
abrir no tópico ↗ 4SPARQL injection é uma preocupação real?dotnetrdf-csharp
Sim. Se você concatena string do usuário dentro da query SPARQL, dá pra injetar padrão que vaza dados, drop triplas (com SPARQL Update), ou trava o engine.
abrir no tópico ↗ SparqlParameterizedString escapa corretamente literais e URIs: cmd.SetUri("color", userColorUri) ou cmd.SetLiteral("name", userName). Mesma defesa que parameterized SQL. Para endpoints públicos, adicionalmente: read-only role no triplestore, timeout por query, rate limiting, e nunca expor SPARQL Update.5Onde rodar SHACL: CI, pipeline ETL, ou runtime de API?dotnetrdf-csharp
Os três, em camadas. CI: valida que o schema (TBox + shapes) é internamente consistente — falha o build se shape referencia classe que não existe. Pipeline ETL: valida dado ingerido antes de comitar no triplestore — reject + alarm. Runtime API: valida payload externo antes de transformar em triplas — rejeição precoce, mensagem clara ao cliente. SHACL em runtime tem custo; reserve para dados externos / não-confiáveis. Para fontes internas validadas no pipeline, não precisa re-validar em runtime.
abrir no tópico ↗ 6Como integrarias um triplestore numa API .NET?dotnetrdf-csharp
Padrão maduro: (1) HttpClient via IHttpClientFactory para conexão com Fuseki/Neptune — handles keep-alive, retry, timeout. (2) SparqlQueryClient wrapper como serviço Scoped/Singleton. (3) Endpoints REST que aceitam DTO, montam
abrir no tópico ↗ SparqlParameterizedString, executam query, mapeiam resultado para DTO de saída. (4) Observability: ActivitySource com tag de query type, log estruturado, métricas de duration. (5) Resilience: Polly com retry + circuit breaker em torno do client. (6) Segurança: queries do user sempre parametrizadas; nunca expor SPARQL Update publicamente; read-only role no triplestore. (7) Caching: Output Caching no ASP.NET Core para queries idempotentes.7Diferença entre OWL e SHACL em uma fraseontology-owl-shacl
OWL infere fatos novos sob mundo aberto; SHACL valida o grafo contra contratos sob mundo fechado. OWL é sobre "o que mais é verdade?", SHACL é sobre "isso aqui está conforme?". Em produção, você usa os dois: OWL para enriquecer o grafo com fatos derivados, SHACL para garantir que dados ingeridos respeitam invariantes do domínio.
abrir no tópico ↗ 8Por que OWL não consegue dizer "Card tem que ter exatamente 1 nome"?ontology-owl-shacl
Consegue dizer (
abrir no tópico ↗ owl:cardinality 1), mas não rejeita dados que violam isso. OWL é declarativo sob OWA: se você afirma :cardA :hasName "X" e :cardA :hasName "Y", reasoner OWL conclui :X owl:sameAs :Y (já que cardinalidade é 1, devem ser o mesmo nome). Nada explode. Se você quer um erro, usa SHACL.9SHACL vs JSON Schema — vale a pena migrar?ontology-owl-shacl
Não migre só por modismo. Você precisa de SHACL se o dado já é RDF — aí JSON Schema não consegue validar (não fala URIs). Para JSON puro, JSON Schema continua sendo a ferramenta. SHACL ganha quando você quer (1) validar grafo com restrições que cruzam triplas (e.g., "se card é creature, então tem power e toughness"), (2) usar SPARQL como constraint avançada, ou (3) reusar shapes da web semântica. Caso contrário, JSON Schema serve.
abrir no tópico ↗ 10"Regra: todo Deck precisa ter ≥99 cartas" — OWL ou SHACL?ontology-owl-shacl
SHACL, porque você quer validação (rejeitar deck inválido), não inferência (deduzir cartas que faltam). OWL sob mundo aberto interpreta um deck com 60 cartas como "as outras 39 ainda não foram afirmadas" — não rejeita. SHACL sob mundo fechado conta as triplas
abrir no tópico ↗ :hasCard existentes e dispara violação se < 99. Implementação: sh:NodeShape ; sh:targetClass :Deck ; sh:property [ sh:path :hasCard ; sh:minCount 99 ]. Resultado: erro estruturado no pipeline de ingestão.11Você usaria OWL e SHACL na mesma pipeline?ontology-owl-shacl
Sim, e é o padrão maduro. SHACL valida ingestão na borda (rejeita dados ruins de fontes externas). Depois, reasoner OWL enriquece o grafo limpo com fatos inferidos (hierarquia, transitividade). Por fim, queries SPARQL operam sobre o grafo materializado. Ordem importa: validar antes de inferir, porque inferir sobre dado ruim faz coisas piores.
abrir no tópico ↗ 12O que é uma tripla RDF?rdf-basics
Uma afirmação em forma sujeito-predicado-objeto onde sujeito e predicado são URIs e o objeto pode ser URI ou literal. É a unidade atômica do RDF: cada fato é uma tripla, e o grafo emerge da união de todas. Diferente de uma row de banco, não há "campos opcionais" — você apenas adiciona/omite triplas. Isso permite que diferentes fontes contribuam para o mesmo recurso sem precisar combinar schemas previamente.
abrir no tópico ↗ 13Por que Turtle e não JSON-LD?rdf-basics
JSON-LD existe e é ótimo para integração com APIs/JavaScript — é só JSON com um
abrir no tópico ↗ @context. Turtle é preferido para autoria humana: muito mais conciso, ponto e vírgula deixa descrever um recurso em poucas linhas, prefixes evitam URIs longas. Equipes que editam ontologias usam Turtle; APIs que transportam usam JSON-LD. Ferramenta como dotNetRDF importa/exporta os dois trivialmente.14URI estável vs URL legível — como escolher?rdf-basics
Não é trade-off — você consegue ambos. URI tem ID estável (UUID ou ID interno) + slug humano opcional:
abrir no tópico ↗ /card/c001-lightning-bolt. Se o nome do card mudar amanhã, você redireciona /card/c001-old-slug → /card/c001-new-slug e a URI canônica permanece. O essencial é que o sistema sempre resolva a URI antiga — quebrar URI é quebrar dados.15TBox e ABox são obrigatórios em RDF?rdf-basics
Não — RDF puro é só triplas. A distinção vem do uso. Você pode ter um grafo só com fatos (puramente ABox) e funcionar. Mas assim que você define classe/hierarquia/restrição (OWL ou RDFS), está no território TBox. Times maduros separam explicitamente porque a vida operacional dos dois é diferente: TBox muda em release, ABox muda em milissegundos.
abrir no tópico ↗ 16Quando NÃO usar RDF?rdf-basics
OLTP transacional pesado, equipe sem experiência, domínio simples já bem servido por relacional, ou quando você precisa de performance bruta sem inferência. RDF tem overhead — cada tripla é uma row em vez de uma coluna. Para um catálogo CRUD comum, SQL é mais simples, mais rápido e mais conhecido. Use RDF quando o shape do dado é grafo: muitas relações heterogêneas, integração de fontes, ou quando inferência semântica agrega valor de negócio. Veja também RDF vs LPG.
abrir no tópico ↗ 17Por que escolheria RDF em vez de Neo4j para um knowledge graph de produto?rdf-vs-lpg
Quatro razões típicas. (1) Integração — precisa unir dados de múltiplas fontes que usam vocabulários públicos (schema.org, Wikidata, GS1). (2) Inferência — quer que "Card é Permanent é Card" seja deduzido automaticamente, não codificado. (3) Padronização — empresa tem política de Linked Data, ou cliente expõe SPARQL endpoint. (4) Validação semântica — SHACL shapes versionadas como contrato de domínio. Se nenhuma dessas se aplica, Neo4j provavelmente é mais simples e mais rápido — não force RDF por estética.
abrir no tópico ↗ 18RDF reification — vale a pena hoje?rdf-vs-lpg
Geralmente não. A reification clássica do RDF (rdf:Statement com subject/predicate/object) é verbosa, custosa, e mata performance de query. Em 2026 use uma destas: (1) RDF-star se sua engine suporta (Jena, GraphDB, Stardog suportam); (2) Named graphs para anotar contexto (graph URI vira "metadados"); (3) Modelagem com nó intermediário — crie um recurso "DeckSlot" com propriedades necessárias. A escolha entre 2 e 3 depende: contexto técnico (proveniência, versão) → named graph; semântica de domínio (slot, papel) → nó intermediário.
abrir no tópico ↗ 19SPARQL ou Cypher — qual é mais simples para travessia de "amigo de amigo"?rdf-vs-lpg
Cypher é mais natural visualmente:
abrir no tópico ↗ MATCH (me)-[:KNOWS]->(:Person)-[:KNOWS]->(friend) RETURN friend — você desenha o caminho. SPARQL faz a mesma coisa via property path: SELECT ?friend WHERE {{ '{' }} :me :knows/:knows ?friend {{ '}' }}. Ambos são concisos. Para profundidade variável ("até 3 saltos"), Cypher: (:me)-[:KNOWS*1..3]->(friend); SPARQL não tem range nativo, precisa UNION ou subqueries. Aqui Cypher ganha sintaxe.20Como você decidiria entre RDF e LPG num projeto novo?rdf-vs-lpg
Sequência de perguntas: (1) Vou integrar com vocabulários públicos? Sim → RDF, é a língua franca. (2) Preciso de inferência (hierarquias, transitividade) sem código de app? Sim → RDF + OWL. (3) Preciso de validação semântica formal (SHACL)? Sim → RDF. (4) É majoritariamente travessia rápida em grafo bem-modelado? Sim → LPG, performance melhor. (5) Tenho time familiar com Cypher? Sim → LPG, custo de adoção menor. Se 1-3 são "não" e 4-5 "sim", LPG. Se 1-3 "sim", RDF (mesmo que 4-5 "sim", pode valer híbrido). Veja também RDF Basics e OWL/SHACL.
abrir no tópico ↗ 21Por que usar Neptune em vez de auto-hospedar Jena + Neo4j separados?rdf-vs-lpg
Trade-off de custo vs ops. Auto-hospedado: menor $ direto, mais flexível (Jena ou GraphDB completo, Neo4j Enterprise), mas você opera HA, backups, scaling, segurança. Neptune: caro, menos features avançadas, mas SLA, backups, VPC, IAM nativos e zero ops. Para POC ou time pequeno em AWS, Neptune acelera. Para escala alta e necessidades específicas (OWL DL, plugins Neo4j), self-hosted ganha. Mesma lógica de RDS vs Postgres em EC2.
abrir no tópico ↗ 22Como escreveria query SPARQL para encontrar todas as cartas vermelhas?sparql-queries
SELECT ?card ?name WHERE {{ '{' }} ?card a mtg:Card ; mtg:hasName ?name ; mtg:hasColor mtg:Red . {{ '}' }} — o padrão de tripla "?card hasColor mtg:Red" filtra implicitamente. Note três coisas: (1) a é atalho para rdf:type; (2) ponto e vírgula encadeia predicados sobre o mesmo sujeito ?card; (3) sem WHERE/JOIN como em SQL — o grafo é navegado pelo pattern. Para cartas que são vermelhas OU azuis, use UNION ou FILTER (?color IN (...)).23Diferença entre FILTER fora e dentro do OPTIONALsparql-queries
Crítico. FILTER dentro do OPTIONAL é parte do critério do match opcional — se falha, o OPTIONAL não casa e variáveis ficam unbound, mas a row principal ainda aparece. FILTER fora aplica sobre o resultado já matched — se falha, a row inteira é eliminada. Exemplo: "Cards com preço < 5 se tiver preço" → FILTER dentro. "Cards onde, se houver preço, deve ser < 5" → também dentro. "Cards com preço definido e < 5" → FILTER fora (você quer eliminar quem não tem). Errar essa distinção é o bug clássico em queries de catálogo opcional.
abrir no tópico ↗ 24Como buscar todos os antepassados de um recurso usando property paths?sparql-queries
Use
abrir no tópico ↗ + ou * sobre o predicado: SELECT ?ancestor WHERE {{ '{' }} :me :hasParent+ ?ancestor {{ '}' }}. O + significa "um ou mais", fazendo travessia transitiva sem precisar definir owl:TransitiveProperty nem usar reasoner. Use * se quiser incluir o próprio :me no resultado. Property paths são uma das melhores adições do SPARQL 1.1 — substitui código de travessia recursiva por uma linha.25Por que minha query SPARQL com agregação dá resultado errado?sparql-queries
Três pitfalls comuns: (1) OPTIONAL com agregação pode gerar binding extra "sem ?var" que vira NULL no SUM/COUNT; use COUNT(DISTINCT ?x) ou COUNT(?x) com FILTER BOUND. (2) Mesma variável em padrões diferentes pode fazer Cartesian sem você perceber — visualize o produto cartesiano dos triple patterns. (3) Triplas duplicadas entre named graphs inflam COUNT — use DISTINCT ou consulte grafo específico com GRAPH ?g. Heurística: se número está "alto demais", normalmente tem JOIN não-intencional.
abrir no tópico ↗ 26CONSTRUCT vs INSERT — quando usar cada?sparql-queries
CONSTRUCT retorna um grafo (não persiste). Use para servir um "view" simplificado a um cliente, ou exportar parte do KG. INSERT persiste no triplestore. INSERT {{ '{' }} ... {{ '}' }} WHERE {{ '{' }} ... {{ '}' }} combina ambos — gera triplas via CONSTRUCT-like e grava. Pattern comum: materializar inferência (rodar reasoner inferencial, escrever resultado em named graph "inferred"), para queries futuras hit no grafo materializado em vez de re-rodar reasoner.
abrir no tópico ↗ 27Como você otimizaria uma query SPARQL lenta?sparql-queries
Passos em ordem: (1) Identificar o gargalo — triplestore tem EXPLAIN? Tempo está em qual padrão? (2) Reordenar padrões — começar pelo mais seletivo (com URI específica). (3) Filtros para dentro — mover FILTER para o escopo mais restrito. (4) Limitar property paths — substituir
abrir no tópico ↗ :rel* por materialização se grafo é estático. (5) Named graph — escopo reduzido se aplicável. (6) Cache — se a query é repetida, materialize. (7) Redesenhar — às vezes a query SPARQL ruim é sintoma de modelagem ruim (cardinalidade alta numa relação). Veja também RDF vs LPG — se sua dor é só performance de travessia, considere LPG.Swift & iOS27
1Por que o Builder é class e não struct?Builder Pattern (Swift)
O Builder precisa ser
abrir no tópico ↗ class para que self retornado nos setters seja
a mesma instância (reference type). Com struct (value type), cada setter retornaria
uma cópia, e o encadeamento não funcionaria corretamente sem mutating + variável
var. Class permite Builder().setA().setB().build() naturalmente.2O que faz @discardableResult?Builder Pattern (Swift)
Suprime o warning do compilador quando o valor de retorno não é usado. Nos setters do Builder,
retornamos
abrir no tópico ↗ Self para permitir encadeamento. Sem @discardableResult,
se alguém chamar builder.setAPIKey("...") sem encadear, o compilador emitiria
"Result of call is unused". A annotation indica que ignorar o retorno é intencional.3Por que usar guard em vez de if-else na validação?Builder Pattern (Swift)
guard força early return — se a condição falha, você DEVE sair do escopo.
Isso evita pirâmides de if-else aninhados e garante que após todos os guards,
os valores estão disponíveis como non-optional. O código fica linear e fácil de seguir:
validações no topo, lógica principal embaixo.4Builder Pattern vs init com default parameters — quando usar cada um?Builder Pattern (Swift)
Init com defaults é suficiente quando: poucos parâmetros (< 5), sem validação cruzada,
e sem necessidade de construção incremental. Exemplo:
Builder é preferível quando: muitos parâmetros opcionais, validação complexa no momento da criação, o objeto precisa ser imutável após criação, ou a construção é incremental (ex: configuração carregada de múltiplas fontes). No IdentityKit, o Builder permite configurar API key, environment, retry policy, e logging separadamente antes de validar tudo junto.
abrir no tópico ↗ init(name: String, age: Int = 0).Builder é preferível quando: muitos parâmetros opcionais, validação complexa no momento da criação, o objeto precisa ser imutável após criação, ou a construção é incremental (ex: configuração carregada de múltiplas fontes). No IdentityKit, o Builder permite configurar API key, environment, retry policy, e logging separadamente antes de validar tudo junto.
5Quando o Builder Pattern é overkill?Builder Pattern (Swift)
Quando o objeto tem poucos parâmetros e todos são obrigatórios. Um
abrir no tópico ↗ struct User {{ '{' }} let name: String; let email: String {{ '}' }}
não precisa de Builder — o memberwise init já é claro. Builder adiciona complexidade (classe extra,
mais código) que só se justifica com muitos opcionais ou validação. Em Swift, considere também
usar struct com mutating methods como alternativa mais leve.6Como garantir que o resultado do Builder é imutável?Builder Pattern (Swift)
O produto (
abrir no tópico ↗ IdentityKitConfiguration) é um struct com todas as properties
let. O Builder é uma class com properties var. O método
build() cria o struct com os valores do builder — após criação, nada pode ser alterado.
O Builder em si pode ser descartado. Padrão: Builder mutável cria resultado imutável.7Como implementar Builder thread-safe em Swift?Builder Pattern (Swift)
Três opções: 1) Marque o Builder como
abrir no tópico ↗ @MainActor — garante que todos
os setters são chamados na main thread. 2) Use actor em vez de class —
setters se tornam async. 3) Documente que o Builder não é thread-safe (comum) —
a construção tipicamente acontece em um único ponto de inicialização. No IdentityKit, o Builder
é usado no setup do app, sempre na main thread.8Builder Pattern em Swift vs Java — quais as diferenças?Builder Pattern (Swift)
Em Java, Builder é quase obrigatório porque a linguagem não tem default parameters nem named arguments.
Em Swift, temos ambos, então Builder é menos comum. Porém, Swift não tem "telescoping constructor"
problem. As vantagens do Builder em Swift são: validação centralizada, fluent API, e separação clara
entre construção (mutável) e uso (imutável). Swift também permite nested types
(
abrir no tópico ↗ Configuration.Builder) que organizam melhor o código.9Qual a diferenca entre structured concurrency e GCD? Por que migrar?Swift Concurrency
Structured concurrency forma uma arvore de tasks onde cancelamento e propagacao de erro
sao automaticos. GCD usa closures soltas que nao tem relacao hierarquica — cancelamento e manual,
erros se perdem facilmente, e data races so aparecem em runtime. Com Swift Concurrency, o compilador
verifica
abrir no tópico ↗ Sendable conformance e garante isolamento de dados em compile-time.
Alem disso, async/await produz stack traces completos, enquanto GCD fragmenta o call stack.10Actor vs Lock — quando usar cada um?Swift Concurrency
Actors isolam estado e serializam acesso automaticamente via await. Sao a escolha padrao
em Swift moderno. Locks (NSLock, os_unfair_lock) sao sincronos e bloqueantes — uteis quando
voce precisa de acesso sincrono sem await (ex: dentro de AVFoundation delegates que nao sao async).
No IdentityKit, usamos actor para
abrir no tópico ↗ ProcessingState e lock para o delegate de
AVCaptureSession porque o callback nao e async.11O que e Sendable e por que o compilador exige?Swift Concurrency
Sendable e um marker protocol que indica que um tipo pode ser transferido entre
contextos de concorrencia (actors, tasks) com seguranca. Value types (structs com propriedades Sendable)
conformam automaticamente. Classes precisam ser final com propriedades imutaveis ou
usar sincronizacao interna. O compilador exige Sendable para prevenir data races em compile-time
sob -strict-concurrency=complete.12Explique @MainActor e quando voce o usa no IdentityKit.Swift Concurrency
@MainActor e um global actor que garante execucao na main thread. Usamos em:
(1) ViewControllers do SDK para updates de UI, (2) o IdentityKitFlowCoordinator que
gerencia navegacao, (3) completion handlers que atualizam UI apos processamento em background.
Marcar uma classe inteira como @MainActor garante que todas as propriedades e metodos
rodam na main thread, eliminando DispatchQueue.main.async manual.13Como funciona o cancelamento cooperativo em Swift Concurrency?Swift Concurrency
Cancelamento e cooperativo — chamar
abrir no tópico ↗ task.cancel() seta um flag mas nao mata a task.
O codigo dentro da task deve checar Task.isCancelled ou usar try Task.checkCancellation()
em pontos estrategicos. APIs do sistema como URLSession.data(for:) ja respeitam cancelamento.
Em withTaskGroup, cancelar o grupo cancela todas as child tasks automaticamente.14Quando usar AsyncStream vs AsyncSequence customizado?Swift Concurrency
AsyncStream e a forma mais simples de criar uma sequencia async — ideal para adaptar
APIs callback-based (delegates, NotificationCenter). Use AsyncSequence customizado quando
precisa de back-pressure, transformacoes complexas ou quando quer um tipo nominado com conformance
especifica. No IdentityKit, usamos AsyncStream para emitir frames processados do pipeline de camera.15Qual a diferenca entre struct e class em Swift? Quando usar cada?Swift Fundamentals
struct e value type (copia ao atribuir, stack allocation, sem heranca, thread-safe por padrao).
class e reference type (compartilha referencia, heap allocation, suporta heranca, precisa de ARC).
Use struct para modelos de dados, DTOs e tipos pequenos. Use class quando precisa de heranca,
identidade de referencia (===), ou interop com Objective-C. No IdentityKit,
abrir no tópico ↗ DocumentScanResult
e struct porque e um value type imutavel passado entre modulos.16O que sao enums com associated values e como diferem de enums em outras linguagens?Swift Fundamentals
Enums em Swift sao algebraic data types (sum types). Cada case pode carregar dados de tipos diferentes,
como
abrir no tópico ↗ case passport(country: String, mrz: String). Isso elimina a necessidade de hierarquias de classes
para modelar variantes. Com switch exhaustivo, o compilador garante que todos os cases sao tratados.
Em C# ou Java, enums sao apenas inteiros nomeados.17Explique Protocol-Oriented Programming. Por que Swift favorece protocols sobre heranca?Swift Fundamentals
POP usa protocols + extensions para definir comportamento compartilhado sem hierarquias rigidas.
Vantagens: (1) structs e enums podem adotar protocols, (2) um tipo pode conformar a multiplos protocols,
(3) extensions fornecem implementacoes padrao sem heranca, (4) melhor para composicao e testabilidade.
No IdentityKit,
abrir no tópico ↗ CaptureProvider e um protocol que permite trocar a implementacao de camera
em testes sem subclassing.18O que e Copy-on-Write (COW) e como Swift o implementa?Swift Fundamentals
COW e uma otimizacao onde value types compartilham storage internamente ate que uma mutacao ocorra.
Somente quando voce modifica a copia, Swift aloca novo storage.
abrir no tópico ↗ Array, Dictionary e
String usam COW nativamente. Para structs customizadas com dados grandes,
voce pode implementar COW manualmente usando uma classe interna como backing storage
e checando isKnownUniquelyReferenced antes de mutar.19Como generics funcionam em Swift? Qual a diferenca entre generics e protocolos com associated types?Swift Fundamentals
Generics parametrizam tipos/funcoes:
abrir no tópico ↗ func decode<T: Decodable>(_ data: Data) -> T.
O chamador escolhe T. Associated types em protocols (associatedtype Element) definem
um tipo abstrato que cada conforming type escolhe. Generics sao resolvidos no call site; associated types
sao resolvidos pelo tipo que conforma. Use generics para funcoes/tipos reutilizaveis e associated types
para protocols que precisam de um tipo placeholder. No IdentityKit, APIRequest<T> usa generics
para tipar a resposta esperada de cada endpoint.20Qual a diferença entre @StateObject e @ObservedObject?SwiftUI & SDK
@StateObject cria e possui o objeto — SwiftUI garante que a instância sobrevive
a re-renders da view. Use quando a view é responsável por criar o ViewModel.
@ObservedObject apenas observa um objeto que vem de fora. Se a parent view re-renderiza, o objeto pode ser recriado (perdendo estado). Use quando o ViewModel é passado como parâmetro. Regra: quem cria usa @StateObject, quem recebe usa @ObservedObject.
abrir no tópico ↗ @ObservedObject apenas observa um objeto que vem de fora. Se a parent view re-renderiza, o objeto pode ser recriado (perdendo estado). Use quando o ViewModel é passado como parâmetro. Regra: quem cria usa @StateObject, quem recebe usa @ObservedObject.
21Por que usar @MainActor no ViewModel?SwiftUI & SDK
Properties
abrir no tópico ↗ @Published que atualizam a UI devem ser modificadas na main thread.
@MainActor garante isso em compile-time — o compilador emite erro se você tentar
acessar de outra thread. Sem ele, uma atualização em background thread causa crash ou comportamento
indefinido. É mais seguro que DispatchQueue.main.async porque é verificado estaticamente.22O que é CheckedContinuation e quando usar?SwiftUI & SDK
CheckedContinuation é a ponte entre código callback-based e async/await.
Você cria com withCheckedThrowingContinuation, e deve chamar resume
exatamente uma vez. "Checked" significa que em debug, o runtime verifica se resume foi chamado
(crash se não foi ou se chamou duas vezes). Use para adaptar delegates, completion handlers,
e APIs legadas para o mundo async/await.23Qual o propósito de @MainActor e como difere de DispatchQueue.main.async?SwiftUI & SDK
@MainActor é uma anotação de compile-time que garante que todo acesso a
properties e métodos marcados aconteça na main thread. O compilador emite erro se detectar
acesso de outro contexto.
abrir no tópico ↗ DispatchQueue.main.async é runtime-only — se esquecer
de usar, o bug só aparece em produção. @MainActor é mais seguro, composable (pode marcar
classe inteira), e integra com Swift concurrency (await automático ao cruzar actor boundary).24Como fazer bridge de delegate pattern para async/await?SwiftUI & SDK
Use
abrir no tópico ↗ withCheckedThrowingContinuation: crie o continuation, passe para um objeto
que implementa o delegate, e chame continuation.resume(returning:) no callback
de sucesso ou resume(throwing:) no callback de erro. Regras: resume deve ser
chamado exatamente uma vez. Para delegates com múltiplos callbacks, use AsyncStream
em vez de continuation.25Quando usar UIKit vs SwiftUI em um SDK?SwiftUI & SDK
UIKit: quando o SDK precisa suportar iOS < 13, precisa de controle fino sobre
lifecycle (viewDidAppear timing), usa câmera/scanner com AVFoundation, ou precisa de animações
complexas customizadas.
SwiftUI: quando o SDK target é iOS 15+, a UI é declarativa e reativa (formulários, status screens), ou quando o app host é SwiftUI e quer integração natural.
Melhor abordagem: lógica em Swift puro (sem UI framework), UI layers para ambos os frameworks, bridge com UIViewControllerRepresentable.
abrir no tópico ↗ SwiftUI: quando o SDK target é iOS 15+, a UI é declarativa e reativa (formulários, status screens), ou quando o app host é SwiftUI e quer integração natural.
Melhor abordagem: lógica em Swift puro (sem UI framework), UI layers para ambos os frameworks, bridge com UIViewControllerRepresentable.
26Como evitar memory leaks com @StateObject e closures?SwiftUI & SDK
1) Use
abrir no tópico ↗ [weak self] em closures que capturam o ViewModel.
2) Em Task {{ '{' }} {{ '}' }} dentro de views, o self (view struct) é copiado,
não retido — sem leak. Mas em ViewModels (class), capture [weak self].
3) Use .task {{ '{' }} {{ '}' }} modifier em vez de onAppear + Task —
o task é automaticamente cancelado quando a view desaparece.
4) Cancele subscriptions no deinit do ViewModel.27Como um SDK deve expor sua API para suportar SwiftUI e UIKit?SwiftUI & SDK
Exponha três camadas: 1) Core: API async/await pura sem UI
(
abrir no tópico ↗ let result = try await sdk.verify()). 2) UIKit: ViewController
com delegate pattern (VerificationDelegate). 3) SwiftUI:
View + ViewModel com @Published states. A core layer é compartilhada — UIKit e SwiftUI
layers são wrappers finos. O desenvolvedor escolhe qual camada usar baseado no seu app.Apple APIs & Tools42
1Como voce processa frames a 30fps sem dropar frames ou bloquear a UI?AVFoundation & Vision
Usamos uma serial queue dedicada para o delegate de video output, separada da main thread e
da session queue. Setamos
abrir no tópico ↗ alwaysDiscardsLateVideoFrames = true para que se o processamento
demorar mais de 33ms, o frame seguinte e descartado em vez de acumular na fila. O Vision request e executado
na mesma queue do delegate. Resultados sao enviados para a UI via @MainActor. Isso garante
que a camera continua fluida mesmo com processamento pesado.2Qual a diferenca entre Vision framework e CoreML para processamento de imagem?AVFoundation & Vision
Vision oferece requests pre-treinados de alto nivel: deteccao de retangulos, faces, texto (OCR),
barcodes — sem precisar de modelo customizado. CoreML executa modelos de ML customizados
(classificacao, segmentacao, etc.). No IdentityKit, usamos Vision para deteccao de bordas de documento
(
abrir no tópico ↗ VNDetectRectanglesRequest) e qualidade facial (VNDetectFaceCaptureQualityRequest).
CoreML seria necessario para, por exemplo, classificar o tipo de documento ou detectar fraudes.3Como voce garante thread safety ao trabalhar com AVFoundation?AVFoundation & Vision
Tres regras: (1) Todas as operacoes de configuracao da session ocorrem em uma serial queue dedicada
(
abrir no tópico ↗ sessionQueue). (2) O delegate de video output recebe callbacks em sua propria
serial queue (videoOutputQueue). (3) Updates de UI sao sempre feitos via
@MainActor. A classe CaptureSessionManager e marcada como
@unchecked Sendable porque a thread safety e garantida pelas serial queues.4Como o VNDetectRectanglesRequest funciona e quais parametros voce ajusta?AVFoundation & Vision
O request detecta retangulos na imagem retornando
abrir no tópico ↗ VNRectangleObservation com 4 pontos
normalizados (0-1). Parametros importantes: minimumConfidence (0.8 no IdentityKit),
minimumAspectRatio e maximumAspectRatio (ajustados para proporcoes de documento),
minimumSize (0.3 para garantir que o documento ocupa area suficiente),
maximumObservations (1, queremos apenas o documento principal).5Como voce implementa a correcao de perspectiva apos detectar o documento?AVFoundation & Vision
Usamos
abrir no tópico ↗ CIFilter com o filtro CIPerspectiveCorrection. Os 4 pontos do
VNRectangleObservation (topLeft, topRight, bottomLeft, bottomRight) sao convertidos
de coordenadas normalizadas para coordenadas da imagem e passados como parametros do filtro.
O resultado e uma imagem retificada com perspectiva corrigida, pronta para OCR ou upload.6Por que usar (capped + jitter) / 2 em vez de apenas capped + jitter?Networking & Resilience
A fórmula
abrir no tópico ↗ (capped + jitter) / 2 garante que o delay médio fique próximo ao valor exponencial
esperado, mas com variação aleatória. Se usássemos apenas soma, o delay médio seria 1.5x o esperado,
desperdiçando tempo. A divisão mantém o comportamento exponencial enquanto distribui os retries no tempo.7Por que usar URLSession.shared vs instância customizada?Networking & Resilience
URLSession.shared é conveniente mas não permite customizar timeout, cache policy,
cert pinning ou headers default. Em SDKs, sempre crie uma instância com URLSessionConfiguration
customizada para controlar comportamento de rede, limitar conexões e configurar TLS.8Como o circuit breaker interage com retry?Networking & Resilience
O circuit breaker é verificado antes de cada tentativa. Se o circuito está Open,
a chamada falha imediatamente sem gastar retry. Isso evita retries inúteis contra um serviço
já degradado. No IdentityKit:
abrir no tópico ↗ try circuitBreaker.preRequest() é chamado antes do
URLSession.data(for:).9Explique exponential backoff com jitter e por que o jitter é necessário.Networking & Resilience
Exponential backoff aumenta o delay entre retries exponencialmente: 1s, 2s, 4s, 8s. Sem jitter,
se 1000 clientes falharem ao mesmo tempo, todos fariam retry nos mesmos instantes — criando picos
sincronizados (thundering herd). O jitter adiciona aleatoriedade ao delay, distribuindo os retries
uniformemente no tempo. A fórmula do IdentityKit
abrir no tópico ↗ (capped + random) / 2 garante que o
delay médio respeita o crescimento exponencial enquanto evita sincronização.10Quais são os três estados do circuit breaker e como transicionam?Networking & Resilience
Closed: estado normal, chamadas passam. Conta falhas consecutivas. Quando falhas
atingem o threshold, transiciona para Open.
Open: rejeita todas as chamadas imediatamente. Após o
Half-Open: permite uma única chamada de teste. Se sucesso, volta para Closed e reseta o contador. Se falha, volta para Open com novo timeout.
abrir no tópico ↗ Open: rejeita todas as chamadas imediatamente. Após o
resetTimeout,
transiciona para Half-Open.Half-Open: permite uma única chamada de teste. Se sucesso, volta para Closed e reseta o contador. Se falha, volta para Open com novo timeout.
11Certificate pinning vs full certificate — qual a diferença e trade-offs?Networking & Resilience
Full certificate pinning: compara o certificado inteiro (DER-encoded). Mais seguro
mas quebra quando o certificado é renovado (tipicamente a cada 90 dias com Let's Encrypt).
Public key pinning: compara apenas a public key (SPKI hash). A key permanece a mesma quando o certificado é renovado, então é mais resiliente a rotação. Porém, se a key é comprometida e uma nova key pair é gerada, requer atualização do app.
Recomendação: pin a public key + tenha um backup pin para rotação de emergência.
abrir no tópico ↗ Public key pinning: compara apenas a public key (SPKI hash). A key permanece a mesma quando o certificado é renovado, então é mais resiliente a rotação. Porém, se a key é comprometida e uma nova key pair é gerada, requer atualização do app.
Recomendação: pin a public key + tenha um backup pin para rotação de emergência.
12Como você testaria retry logic sem depender de rede real?Networking & Resilience
Usando
abrir no tópico ↗ MockURLProtocol que intercepta todas as chamadas de URLSession. Configure
respostas sequenciais: primeira retorna 503, segunda retorna 503, terceira retorna 200. Verifique
que o client faz 3 chamadas e retorna sucesso. Também valide que os delays entre tentativas
seguem o padrão exponencial usando XCTAssert nos timestamps.13Quando NÃO fazer retry de uma requisição?Networking & Resilience
Nunca faça retry de: 4xx errors (exceto 408 e 429) — são erros do client e repetir
não vai mudar o resultado. POST/PUT não-idempotentes — podem causar duplicação.
Erros de autenticação (401/403) — precisa renovar token primeiro.
Payload inválido (400) — o request está malformado. No IdentityKit,
abrir no tópico ↗ isRetryable(statusCode:) filtra apenas 408, 429, 500, 502, 503 e 504.14Como implementar timeout por tentativa vs timeout total?Networking & Resilience
Per-try timeout: configurado no
Total timeout: implementado com
abrir no tópico ↗ URLSessionConfiguration.timeoutIntervalForRequest,
limita cada tentativa individual. Se a tentativa demora mais, cancela e tenta novamente.Total timeout: implementado com
Task.sleep + Task.cancel() ou
withThrowingTaskGroup, limita o tempo total incluindo todos os retries. Se o total expira,
toda a operação falha mesmo que retries ainda restem.15Qual a diferença entre Keychain e UserDefaults para armazenamento?Security & Keychain
UserDefaults armazena em plist no filesystem — não é criptografado, qualquer processo
com acesso ao sandbox pode ler. Keychain é criptografado pelo Secure Enclave,
protegido por hardware, e sobrevive à desinstalação do app (a menos que use
abrir no tópico ↗ ThisDeviceOnly).
Nunca armazene tokens, senhas ou chaves em UserDefaults.16Como tratar errSecDuplicateItem no Keychain?Security & Keychain
Ao chamar
abrir no tópico ↗ SecItemAdd, se o item já existe, retorna errSecDuplicateItem.
A abordagem correta é: tente adicionar primeiro. Se receber duplicate, faça SecItemUpdate
com o mesmo query. Não delete e re-adicione — isso causa janela onde o item não existe.17O que acontece se todos os pins falharem?Security & Keychain
A conexão é rejeitada com
abrir no tópico ↗ .cancelAuthenticationChallenge. O SDK deve ter mecanismo
de fallback: reportar o erro, tentar buscar novos pins de um endpoint confiável (bootstrapped),
ou falhar graciosamente. Nunca desabilite pinning silenciosamente — isso anula toda a proteção.18Por que usar kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly em um SDK?Security & Keychain
AfterFirstUnlock: o item fica disponível após o primeiro desbloqueio do dispositivo,
mesmo com a tela bloqueada. Isso é essencial para SDKs que precisam fazer requests em background
(push notifications, background refresh).
ThisDeviceOnly: impede que o item seja incluído em backups (iCloud, iTunes) e migrado para outro dispositivo. Tokens de autenticação são específicos ao dispositivo — se migrassem, poderiam ser usados em dispositivo comprometido. A combinação oferece disponibilidade + segurança.
abrir no tópico ↗ ThisDeviceOnly: impede que o item seja incluído em backups (iCloud, iTunes) e migrado para outro dispositivo. Tokens de autenticação são específicos ao dispositivo — se migrassem, poderiam ser usados em dispositivo comprometido. A combinação oferece disponibilidade + segurança.
19Public key pinning vs certificate pinning — qual escolher?Security & Keychain
Public key pinning é preferível na maioria dos casos. Quando o certificado é renovado
(a cada 90 dias com Let's Encrypt), a public key geralmente permanece a mesma. Com certificate pinning,
cada renovação quebraria o app e exigiria atualização. Public key pinning sobrevive a renovações
normais e só quebra se o key pair inteiro for regenerado — evento raro e planejável.
abrir no tópico ↗ 20Como proteger dados sensíveis em memória (não apenas em repouso)?Security & Keychain
Dados em memória são vulneráveis a memory dumps e debugging. Estratégias:
1) Zero-out buffers após uso com
abrir no tópico ↗ memset_s (não otimizado pelo compilador).
2) Use Data com escopo limitado e não armazene em properties longevas.
3) Desabilite screenshots com UITextField.isSecureTextEntry.
4) Detecte jailbreak/debugging e limpe dados sensíveis.21O que é ATS e quando é aceitável desabilitá-lo?Security & Keychain
App Transport Security força HTTPS com TLS 1.2+ para todas as conexões. Desabilitar com
abrir no tópico ↗ NSAllowsArbitraryLoads requer justificativa na App Review. Casos aceitáveis:
comunicação com hardware local (impressoras, IoT), mídia de terceiros sem HTTPS,
ou webview carregando conteúdo arbitrário. Mesmo nesses casos, prefira exceções por domínio
(NSExceptionDomains) em vez de desabilitar globalmente.22Como o Keychain sobrevive à desinstalação do app?Security & Keychain
Por padrão, items do Keychain persistem após desinstalação do app — isso é by design da Apple.
Na reinstalação, o app pode ler items antigos. Isso pode ser problema de segurança: use
abrir no tópico ↗ ThisDeviceOnly e verifique no primeiro launch se há items órfãos para limpar.
A flag kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly é deletada automaticamente
quando o usuário remove o passcode.23Como voce define module boundaries em um SDK iOS?SPM & Modules
Cada modulo deve ter uma responsabilidade unica e expor uma API publica minima.
No IdentityKit: Core contem models e protocols (zero dependencias de framework), Capture lida com
AVFoundation, UI com ViewControllers, Network com URLSession. A regra: dependencias apontam para Core,
nunca ao contrario. Usamos
abrir no tópico ↗ package access level (Swift 5.9) para tipos compartilhados
entre modulos sem expor ao integrador.24Explique a diferenca entre source distribution e binary distribution via SPM.SPM & Modules
Source: o Package.swift aponta para targets com codigo Swift. O integrador compila junto
com o app. Vantagem: debug symbols, stepping into code. Desvantagem: expoe codigo, build mais lento.
Binary: XCFramework pre-compilado referenciado via
abrir no tópico ↗ binaryTarget(url:checksum:).
Vantagem: protege IP, build rapido. Desvantagem: precisa compilar para todas as arquiteturas
(arm64 device, arm64 simulator, x86_64 simulator) e assinar com code signature.25O que e o flag -strict-concurrency=complete e por que usar em cada modulo?SPM & Modules
Este flag ativa todas as verificacoes de Sendable e isolamento de actors em compile-time.
Em modo
abrir no tópico ↗ complete, o compilador trata warnings de concorrencia como erros.
Isso garante que nenhum data race passe pela revisao de codigo. No IdentityKit, habilitamos
em cada target via .enableExperimentalFeature("StrictConcurrency") no Package.swift
para pegar problemas cedo, antes que o integrador atualize seu projeto.26Como voce lida com versionamento de um SDK distribuido via SPM?SPM & Modules
Seguimos Semantic Versioning: major para breaking changes na API publica, minor para
features novas retrocompativeis, patch para bug fixes. SPM usa git tags para resolucao de versoes.
Mantemos um CHANGELOG e usamos
abrir no tópico ↗ @available annotations para deprecar APIs antes de remover.
Para XCFramework, o checksum no Package.swift garante integridade do binario.27Quais sao as limitacoes de SPM comparado a CocoaPods para SDKs?SPM & Modules
SPM nao suporta: (1) resources em binary targets facilmente (precisa de resource bundle separado),
(2) build settings customizados por consumer (CocoaPods tem xcconfig), (3) subspecs granulares
(SPM usa products/targets mas sem a mesma flexibilidade). Vantagens do SPM: nativo no Xcode,
sem ruby dependency, melhor resolucao de conflitos, suporte a plugins de build.
abrir no tópico ↗ 28Qual a diferença entre XCTest e o novo Swift Testing framework?Swift Testing & CI/CD
Swift Testing (iOS 16+) usa macros
abrir no tópico ↗ #expect e @Test em vez de
XCTAssert e prefixo test. Suporta parameterized tests nativamente,
tags para organização, e melhor integração com Swift concurrency. XCTest ainda é necessário
para UI tests e apps que suportam iOS < 16.29Como testar código que usa Task.sleep sem esperar o delay real?Swift Testing & CI/CD
Injete uma abstração de clock/delay. Em vez de chamar
abrir no tópico ↗ Task.sleep diretamente,
use um protocolo Delaying com método delay(for:). No teste, passe
uma implementação mock que retorna imediatamente. Isso evita testes lentos e flaky por timing.30Por que usar URLProtocol em vez de injetar um protocol de networking?Swift Testing & CI/CD
MockURLProtocol intercepta no nível do URL loading system — testa o código real
de URLSession incluindo serialização de request, headers, e response parsing. Um protocol
injetado (ex: HTTPClient) testa apenas a lógica acima do networking, podendo
perder bugs na construção do request ou parsing do response. O ideal é usar ambos:
URLProtocol para integration tests, protocols para unit tests isolados.31Como cachear dependências no CI para builds mais rápidos?Swift Testing & CI/CD
Use
abrir no tópico ↗ actions/cache com key baseada no hash do Package.resolved.
Cachear DerivedData também ajuda, mas cuidado com invalidação — use o hash
dos source files como key. Para SPM, cachear .build directory.
Fastlane tem plugin fastlane-plugin-cache para isso.32Como testar networking sem depender de rede real?Swift Testing & CI/CD
Três abordagens: 1) MockURLProtocol: registra uma subclasse de URLProtocol que
intercepta requests e retorna respostas pré-configuradas. Testa o stack real de URLSession.
2) Protocol injection: define
abrir no tópico ↗ protocol HTTPClient e injeta mock no teste.
3) URLSession com configuration.protocolClasses: configura o mock apenas na session
do teste, sem afetar outras sessions.33Como testar thread safety em Swift?Swift Testing & CI/CD
1) Use
abrir no tópico ↗ DispatchQueue.concurrentPerform para simular acesso concorrente.
2) Thread Sanitizer (TSan) no Xcode detecta data races — habilite em Test scheme.
3) Para actors, teste com múltiplas Tasks concorrentes e verifique consistência.
4) XCTestExpectation com expectedFulfillmentCount para
validar que N operações concorrentes completam corretamente.34Qual a estratégia de CI ideal para um SDK iOS?Swift Testing & CI/CD
PR checks: build + unit tests + linting (SwiftLint) em cada PR.
Nightly: integration tests completos + multiple Xcode versions + simuladores diferentes.
Release: build universal XCFramework + run full test suite + generate docs (DocC) +
tag + publicar no SPM registry. Use matrix strategy para testar iOS 15, 16, 17 simultaneamente.
abrir no tópico ↗ 35Como lidar com testes flaky no CI?Swift Testing & CI/CD
1) Identifique a causa: timing issues (use expectations em vez de sleep),
shared state (reset em setUp/tearDown), ou dependência de ordem de execução.
2) XCTest tem
abrir no tópico ↗ XCTSkipIf para pular condicionalmente.
3) Marque testes flaky com tag e execute em lane separada.
4) Retry automático no CI: xcodebuild test -retry-tests-on-failure (Xcode 15+).36O que é test double e quais tipos existem?Swift Testing & CI/CD
Dummy: preenche parâmetro mas não é usado.
Stub: retorna valores pré-definidos (MockURLProtocol é um stub).
Spy: registra como foi chamado para verificação posterior.
Mock: verifica expectativas de chamada (método X chamado N vezes).
Fake: implementação funcional simplificada (banco in-memory).
Em Swift, protocols facilitam criar qualquer tipo de test double.
abrir no tópico ↗ 37Por que usar Coordinator em vez de UINavigationController diretamente num SDK?UIKit Patterns
O SDK nao controla o navigation stack do app host — push/pop pode conflitar com a navegacao do app.
Com Coordinator, o SDK apresenta seu proprio UINavigationController modalmente,
gerenciando o fluxo internamente. Quando termina, faz dismiss e retorna o resultado via delegate.
Isso garante isolamento total: o app host nao precisa saber quantos steps o SDK tem,
e o SDK nao precisa saber nada sobre a estrutura de navegacao do app.
abrir no tópico ↗ 38Quais sao os requisitos de acessibilidade para um SDK de verificacao de identidade?UIKit Patterns
(1) VoiceOver: todos os botoes e instrucoes com accessibilityLabel descritivo,
announcements para mudancas de estado. (2) Dynamic Type: textos que escalam com
preferencias do usuario usando preferredFont. (3) Reduce Motion: desativar animacoes
complexas quando isReduceMotionEnabled. (4) Color contrast: ratio minimo de 4.5:1
para texto normal, 3:1 para texto grande. (5) Switch Control: todos os elementos
interativos acessiveis via scanning.
abrir no tópico ↗ 39Como voce implementa Dynamic Type em Auto Layout programatico?UIKit Patterns
Usamos
abrir no tópico ↗ UIFont.preferredFont(forTextStyle:) e setamos
adjustsFontForContentSizeCategory = true em labels. Para constraints, usamos
valores relativos e UIFontMetrics para escalar spacings:
UIFontMetrics.default.scaledValue(for: 16). O layout usa
UIStackView que redistribui subviews automaticamente quando textos crescem.
Testamos com todos os tamanhos de texto no Accessibility Inspector.40Como voce garante que o coordinator limpa recursos corretamente?UIKit Patterns
O coordinator mantem weak references para ViewControllers e usa o padrao de
abrir no tópico ↗ finish() que: (1) para a camera session, (2) cancela tasks async pendentes,
(3) faz dismiss do navigation controller, (4) chama o delegate com o resultado ou erro.
Se o usuario dismiss manualmente (swipe down), o presentationControllerDidDismiss
delegate chama finish(with: .cancelled) para cleanup.41Por que preferir Auto Layout programatico sobre storyboards em SDKs?UIKit Patterns
(1) Distribuicao: storyboards precisam de resource bundles — problematico em SPM
e XCFramework. (2) Code review: constraints em codigo sao legiveis em PRs,
storyboard XML e opaco. (3) Conflitos: storyboards geram conflitos de merge
frequentes em equipe. (4) Performance: sem desserializacao de XIB.
(5) Controle: facil criar layouts condicionais (iPhone vs iPad, Dynamic Type).
abrir no tópico ↗ 42Como voce testa o fluxo do coordinator sem UI?UIKit Patterns
Injetamos dependencias via protocols:
abrir no tópico ↗ CaptureProvider (mock da camera),
AnalysisProvider (mock do Vision), NetworkProvider (mock do upload).
O coordinator recebe essas dependencias no init. Nos testes, usamos mocks que retornam
resultados pre-definidos e verificamos que o coordinator transiciona para os steps corretos
e chama o delegate com os resultados esperados — tudo sem instanciar UIKit.