Porque falham os sistemas embebidos em produção
Os sistemas embebidos parecem muitas vezes estáveis até saírem de ambientes controlados. O problema é que os sistemas embebidos atuais já não são dispositivos isolados. São ecossistemas de componentes que interagem entre si.

Muitos sistemas embebidos parecem estáveis até ao momento em que saem de ambientes controlados.
Dentro da empresa, tudo pode parecer bem durante meses. O dispositivo funciona na validação interna, porta-se bem nas demonstrações e passa os ciclos de teste habituais. Depois a produção começa a crescer e surgem coisas estranhas:
dispositivos que bloqueiam depois de vários dias ligados
instabilidade de rede que só aparece com certos padrões de tráfego
falhas raras de temporização que ninguém consegue reproduzir de forma consistente
degradação aleatória depois de atualizações de firmware
comportamento que muda consoante a revisão do hardware
As equipas que trabalham com sistemas embebidos conhecem bem este padrão.
Em geral, o problema não é um bug óbvio escondido no código à espera de ser descoberto. Os sistemas embebidos atuais já não são dispositivos isolados com comportamento previsível. São ecossistemas de componentes que interagem entre si, em condições que mudam a toda a hora.
A maioria das falhas surge entre os componentes, e não dentro deles
É aqui que o desenvolvimento embebido engana. Cada parte do sistema pode funcionar muito bem sozinha:
Drivers
Comportam-se corretamente isolados, mas podem gerar estados inesperados com acesso concorrente
Serviços
Cada um devolve os resultados esperados, mas a temporização entre eles cria instabilidade em escala
Firmware
Passa a validação nos ambientes de teste, mas comporta-se de outra forma no hardware real
Comunicação em rede
Parece estável nos testes, mas falha com padrões de tráfego reais e com a infraestrutura sob pressão
Mas os sistemas em produção são definidos pela interação entre as partes.
Um pequeno atraso numa camada pode provocar novas tentativas noutro sítio. Um timeout de rede muda a temporização da execução. Um componente sobrecarregado cria instabilidade muito longe da origem do problema.
Quanto maior a infraestrutura, mais difícil é ver estas cadeias de interação com clareza. Isto é ainda mais verdade hoje. Os ambientes embebidos incluem cada vez mais infraestrutura distribuída, serviços cloud, atualizações remotas, camadas de rede e integrações externas.
A certa altura, o sistema deixa de se comportar como um conjunto de módulos. Passa a comportar-se como um ecossistema com padrões de falha próprios. Em geral, é aí que os testes tradicionais começam a ter dificuldades.
Os bugs embebidos raramente se comportam como bugs de software comuns
A depuração embebida é tão frustrante, em parte, porque muitas falhas estão muito ligadas às condições de execução. Uma condição de corrida pode aparecer apenas:
depois de vários dias ligado
com padrões de tráfego invulgares
durante eventos de hardware simultâneos
em certas revisões do dispositivo
quando os recursos estão no limite
Por isso, os engenheiros passam imenso tempo a perseguir comportamentos que, vistos de fora, parecem aleatórios.
O bug desaparece quando se aumenta o logging
As ferramentas de observabilidade mudam a temporização da execução e escondem as condições que provocam a falha
O problema só aparece na instalação de um cliente
Certas configurações de hardware, padrões de carga ou condições do ambiente não se conseguem reproduzir internamente
Um reinício resolve tudo, por algum tempo
O estado acumula-se com o tempo até passar um limiar. A causa raiz fica escondida entre reinícios
O mesmo binário comporta-se de forma diferente em hardware idêntico
Pequenas diferenças na revisão do firmware, na disposição da memória ou na ordem de inicialização mudam o comportamento em execução
Estas situações são muito comuns em ambientes embebidos. A temporização, a concorrência e a interação com o hardware afetam o comportamento a todo o momento. Quando a rede entra em jogo, reproduzir o problema fica ainda mais difícil.
Os ambientes de produção expõem pressupostos que ficaram escondidos durante o desenvolvimento
Os ambientes de desenvolvimento costumam ser muito mais limpos do que as instalações reais. Os sistemas internos têm, em geral, tráfego previsível, hardware estável, tempo de funcionamento controlado e infraestrutura simplificada.
Em produção, essas proteções desaparecem. Os sistemas passam a funcionar sem parar, com carga irregular. A infraestrutura muda com o tempo. Os dispositivos derivam para estados inesperados. As dependências externas ficam instáveis. Os padrões de tráfego deixam de ser "normais".
Pequenos compromissos técnicos que pareciam inofensivos no desenvolvimento começam a acumular-se e geram instabilidade em todo o sistema. Essa acumulação é importante.
As falhas embebidas raramente são acontecimentos súbitos e catastróficos. Formam-se aos poucos:
A pressão sobre a memória cresce devagar
As fugas e a fragmentação acumulam-se durante dias até o sistema ficar sem margem
As novas tentativas ficam mais frequentes
Uma pequena instabilidade numa camada provoca novas tentativas que aumentam a carga no resto do sistema
A sincronização enfraquece sob carga
Pressupostos de temporização que funcionavam em condições normais falham quando a carga se aproxima dos limites do sistema
As filas crescem nos picos de tráfego
Os buffers enchem mais depressa do que esvaziam e acabam por causar timeouts e mensagens perdidas
A latência altera pressupostos de temporização noutros pontos
Um abrandamento num componente empurra para fora dos limites esperados o comportamento dependente de temporização noutras partes do sistema
O sistema pode continuar a parecer operacional enquanto a fiabilidade se degrada em silêncio.
Os testes mudaram muito mais devagar do que a infraestrutura
Muitas metodologias de teste ainda vêm de uma época em que os lançamentos tinham fases mais claras: desenvolvimento, testes, implantação.
Os sistemas embebidos atuais já não evoluem assim. A infraestrutura muda sem parar:
os serviços atualizam-se de forma independente
os pipelines de implantação passam a ser automáticos
o firmware evolui depressa
as dependências multiplicam-se
as condições de execução mudam a toda a hora
Mas muitos processos de validação ainda partem do princípio de que os sistemas ficam quase iguais entre versões. Este desfasamento cria pontos cegos. Isto acontece sobretudo em sistemas grandes, onde já ninguém vê por completo como todos os componentes se comportam em conjunto.
O mais difícil já não é escrever o código
As equipas atuais constroem sistemas muito sofisticados com uma rapidez surpreendente. O difícil, agora, é manter a visão do sistema quando ele fica grande o suficiente:
Várias camadas de hardware
Cada camada traz a sua própria temporização, o seu estado e os seus modos de falha, que interagem com tudo o que está acima e abaixo
Serviços distribuídos
As fronteiras entre serviços criam passagens invisíveis, onde os pressupostos de cada equipa geram comportamentos inesperados
Comportamento assíncrono
Os eventos disparam em sequências difíceis de prever, de reproduzir ou de seguir até à origem
Dependências de infraestrutura externa
Os serviços de terceiros, as APIs cloud e a infraestrutura de rede trazem uma variabilidade que os testes internos não conseguem modelar
A esta escala, as falhas deixam de parecer erros de engenharia isolados. Passam a parecer comportamento que nasce do próprio sistema.
Isto muda o foco dos testes. A pergunta já não é se cada componente funciona tecnicamente. A pergunta é se o ambiente, como um todo, se mantém estável em condições reais.
Os sistemas embebidos estão cada vez mais difíceis de perceber
Uma das maiores mudanças em curso é que o software embebido se comporta cada vez mais como infraestrutura. Os dispositivos já não são pontos isolados que correm o mesmo firmware durante anos. Muitos sistemas funcionam hoje como ambientes em evolução contínua, ligados a ecossistemas maiores.
Por isso, os testes também têm de se aproximar do comportamento em execução:
Validação que conhece a infraestrutura
Testes que têm em conta o ambiente completo, para lá do dispositivo isolado
Ambientes reproduzíveis
Simular condições realistas para encontrar as falhas antes de chegarem à produção
Testes contínuos durante o desenvolvimento
Validação feita a par do desenvolvimento, em vez de numa barreira final antes do lançamento
Análise das interações entre componentes
Perceber como os componentes se comportam em conjunto em condições reais
Quando as falhas se tornam visíveis em produção, a instabilidade de fundo pode já existir há meses. E, nos sistemas embebidos, estes problemas raramente ficam isolados durante muito tempo.