Seguidores

segunda-feira, 25 de maio de 2026

Exemplo 3 de uso do MASP com IA em TI - O Mistério do Botão "Sair" - 4 Lições Cruciais sobre Consistência em Apps Mobile

 O exemplo 3 de uso do MASP (Método de Análise e Solução de Problemas), com IA (Inteligência Artificial) em TI (Tecnologia da Informação), apresenta uma situação de teste incompleto que causou uma falha num aplicativo.


Exemplo de Prompt estruturado com MASP

1CONTEXTO

1.1 Correção de bug no app VoteNet, na Beta v2.28m

1.1.1 Descrição do bug: No celular, não se consegue chegar ao “Sair”, no menu à esquerda.

1.1.2 Frontend: 

1.1.2.1 ReactJS v15.6.1

1.1.2.2 Componente: Header.jsx e SideBar.jsx

1.1.2.2 Node v6.17.1 

1.1.2.3 NPM v3.10.10

1.1.3 Axios v.0.26.0: comunicação via API, chamando funções do arquivo ajax_utils.js.

1.3.1.1 Função: ?

1.1.4 Backend: Django v4.2.3
1.1.4.1 Aplicação: ?

1.1.4.2 model.py: ?

2IDENTIFICAÇÃO DO PROBLEMA

Um problema é caracterizado pela diferença entre o que é e o que deveria ser. As perguntas abaixo (5W1H) ajudarão a caracterizar o problema, com detalhes do cenário.

[What?] O que está acontecendo?

Está acontecendo um bug no aplicativo VoteNet, na Beta v.2.28l.

[Who?] Quem está envolvido?

Quem está envolvido é o usuário responsável pela homologação.

[When?] Quando ocorreu?

Ocorreu quando o usuário clicou no hamburger button e procurou a opção “Sair”, que é a última. 

[Where?] Onde aconteceu?

Aconteceu no celular, no componente SideBar.jsx.

[Why?] Por que isso é um problema?

Porque o usuário não consegue ver a opção “Sair” do menu principal vertical, emitido pelo componente SideBar.jsx.

[How?] Como surgiu?

Surgiu após o login no celular. No Desktop, onde o menu é horizontal, emitido pelo componente Header.jsx, não houve problema.

3Observação

Investigue características específicas do problema usando o Diagrama de Ishikawa (Causa e Efeito). Registre “SIM”, “NÃO” ou “NÃO SEI”, para fatores como:

3.1. Método (A Lógica)

  • Regras de Negócio: O algoritmo está aplicando a regra correta? NÃO

  • Fluxo do Processo: Existe algum "nó" na sequência da automação (ex: um passo que tenta rodar antes do anterior terminar)? NÃO SEI

  • Tratamento de Erros: O código sabe o que fazer quando recebe uma resposta inesperada? NÃO SEI

3.2. Máquina (Infraestrutura/Software)

  • Ambiente: O erro ocorre em produção, mas não em homologação? NÃO (Ocorre em homologação e não em desenvolvimento)

  • Recursos: Há falta de memória, timeout de servidor ou falha na conexão com o banco de dados? NÃO SEI

  • Versões: Bibliotecas ou frameworks desatualizados estão gerando conflitos? NÃO SEI, mas esta é uma hipótese a ser analisada.

3.3. Mão de Obra (Intervenção Humana)

  • Desenvolvimento: Houve falha na escrita do código ou falta de revisão (code review)? SIM (foi adicionada uma nova opção, Mensagens, de forma incompleta). 

  • Testes: O cenário que gerou o bug foi previsto no plano de testes? NÃO

  • Operação: Alguém alterou uma configuração manualmente que quebrou a automação? SIM (o código foi alterado no menu horizontal e no menu vertical, mas testado apenas no Desktop do desenvolvedor, que mostra o menu horizontal, antes de publicar para homologação).

3.4. Medida (Dados e Monitoramento)

  • Input de Dados: O bug é causado por um caractere especial ou campo vazio que a automação não esperava? NÃO

  • Logs: O sistema está gerando registros suficientes para rastrear onde o erro começa? NÃO SEI

  • Métricas: O erro acontece após um número X de execuções (vazamento de memória)? NÃO

3.5. Material (Integrações/APIs)

  • Dependências Externas: Uma API de terceiros mudou o formato do JSON ou está fora do ar? NÃO SEI

  • Qualidade dos Dados: A base de dados que alimenta a automação contém informações corrompidas? NÃO (é a mesma que foi usada no teste do desenvolvedor)

3.6. Meio Ambiente (Contexto de Execução)

  1. Conectividade: Instabilidade na rede afetou a troca de pacotes? NÃO SEI

  2. Simultaneidade: Outro processo automatizado está tentando acessar o mesmo recurso ao mesmo tempo? NÃO SEI

4Análise

Para cada fator identificado na etapa anterior, utilize os “5 Porquês” para encontrar a causa raiz.

4.1 Método (A Lógica)

Por quê? NÃO SEI

4.2 Máquina (Infraestrutura/Software)

Por quê? NÃO SEI

4.3 Mão de Obra (Intervenção Humana)

Por quê? Porque a opção do menu identificada por “Mensagens” foi adicionada e o comportamento implementado de forma incompleta.

Por quê? Porque no componente Header.jsx (menu horizontal, no Desktop) o comportamento ao clicar em “Mensagens” foi testado e no componente SideBar.jsx (menu vertical, no celular) não foi testado.

Por quê? Porque durante os testes no Desktop houve vários erros e o foco foi direcionado, totalmente, para o comportamento no Header.jsx.

Por quê? Porque quando os testes passaram no Desktop, os testes foram considerados concluídos e a versão Beta v.2.28i foi publicada para homologação.

Por quê? Porque o desenvolvedor esqueceu que faltavam os testes no celular. Resumindo, o comportamento ao clicar na opção “Mensagens” deve sinalizar a quantidade de mensagens não lidas com um ícone de círculo vermelho, contendo a quantidade de mensagens não lidas no centro do círculo. Este comportamento foi implementado no componente Header.jsx mas não foi implementado no SideBar.jsx, por esquecimento. No SideBar.jsx foi apenas adicionada a opção Mensagens, sem o ícone de formato circular, que contém a quantidade de mensagens não lidas. Portanto, houve falha na escrita do código, na revisão e nos testes.

4.4 Medida (Dados e Monitoramento)

Por quê? NÃO SEI

4.5 Material (Integrações/APIs)

Por quê? NÃO SEI

4.6 Meio Ambiente (Contexto de Execução)

Por quê? NÃO SEI

5Comando 

Atue como um Especialista em Qualidade de Software e Metodologia MASP (Método de Análise e Solução de Problemas). 

Estou analisando um bug em um processo automatizado dentro do aplicativo VoteNet, implementado nos componentes Header.jsx e SideBar.jsx, em anexo. 

Com base nas 3 primeiras etapas do MASP especificadas acima, elabore a etapa 4, "Plano de Ação" (formato 5W2H), focando no bloqueio e correção definitiva das causas do bug. O comportamento ao clicar na opção “Mensagens” do menu deve ser o mesmo, tanto no componente Header.jsx como no SideBar.jsx.

Resultado do Prompt estruturado com MASP

Aqui está a continuidade da sua análise MASP, avançando para a elaboração do Plano de Ação, seguido do código corrigido.

Como estamos garantindo a compatibilidade estrita com o React 15.6.1, mantivemos a estrutura de componentes de classe (Class Components) e removemos tentativas de importação de hooks (como o useLocation que constava no SideBar.jsx original mas não era suportado ou utilizado nesta versão).

Etapa 4: Plano de Ação (5W2H)

Diretriz

Descrição

What? (O que será feito)

Sincronizar o comportamento da opção "Mensagens" no SideBar.jsx (mobile) com o Header.jsx (desktop), implementando a busca e a renderização do badge (círculo vermelho) de mensagens não lidas. Corrigir também a importação ausente do getAccessToken que quebraria a submissão do modal.

Why? (Por que será feito)

Para padronizar a UI/UX entre plataformas e garantir que a ausência de estado/variáveis não quebre a renderização do menu lateral, permitindo que o usuário consiga rolar até a opção final "Sair".

Where? (Onde será feito)

No arquivo de frontend SideBar.jsx.

When? (Quando será feito)

Imediatamente, para integração na versão Beta v2.28m.

Who? (Quem fará)

Desenvolvedor Frontend.

How? (Como será feito)

1. Importar o ajax e getAccessToken.


2. Adicionar unreadCount: 0 ao estado inicial.


3. Criar a função _updateUnreadCount com a chamada de API.


4. Invocar a função e escutar o evento messageRead no componentDidMount.


5. Atualizar o JSX substituindo a tag <a> solta por um <Link> espelhando a mesma lógica de CSS inline usada no Header.

How Much? (Quanto custará)

Apenas tempo de desenvolvimento e novos testes focados na visualização Mobile (aprox. 1 hora).

Correção de Código: SideBar.jsx

Abaixo está o código atualizado do SideBar.jsx. As lógicas de evento global (window.addEventListener('messageRead', ...)), requisição Ajax e a estrutura do badge vermelho foram replicadas exatamente como no Header.




JavaScript

import React from 'react';
import { Link } from 'react-router';
import ajax from '../../ajax-utils'; // Adicionado import do ajax
import { logout, getLogedUser, isLoggedIn, getAccessToken } from '../../utils/AuthService'; // Adicionado getAccessToken ausente

import { Modal, Button } from 'react-materialize';

class SideBar extends React.Component{
    constructor(props){
        super(props);
        this.state = {
          user: props.user,
          signup_profile: false,
          unreadCount: 0 // Estado inicial para o badge
        };
        this._handleLogout = this._handleLogout.bind(this);
        this.showFaleConoscoModal = this.showFaleConoscoModal.bind(this);
        this._handleFaleConoscoSubmit = this._handleFaleConoscoSubmit.bind(this);
        this._updateUnreadCount = this._updateUnreadCount.bind(this); // Bind do novo método
    }

    componentDidMount() {
        const { pathname } = window.location;
       
        if (pathname != '/signup-profile'){
            this.setState({user: getLogedUser()}, () => {
                this._updateUnreadCount(); // Busca inicial de mensagens não lidas
            });
        }
       
        if (pathname == '/signup-profile'){
            this.setState({signup_profile: true});
        }

        // Ouvinte global: atualiza o sidebar quando a mensagem for lida
        window.addEventListener('messageRead', this._updateUnreadCount);
    }

    componentWillUnmount() {
        // Boa prática: limpar o listener ao desmontar para evitar vazamento de memória
        window.removeEventListener('messageRead', this._updateUnreadCount);
    }

    _updateUnreadCount() {
        ajax.getUnreadCount().then(data => {
            if (data && data.unread_count !== undefined) {
                this.setState({ unreadCount: data.unread_count });
            }
        });
    }

    showFaleConoscoModal () {
        this.setState({
            detail:{},
            whatsapp_link:false,
            whatsapp:false,
            invite_by:false
        });
   
        $('#faleConoscoModal').modal('open');
    }

    _handleFaleConoscoSubmit(event) {
        event.preventDefault();
        let {value: mensagem} = this._mensagem;
        this.setState({email_sended:true});
       
        // getAccessToken agora está corretamente importado
        ajax.mensagemFaleconosco(mensagem, getAccessToken()).then(data => {
          let {err} = data;
          this.setState({email_sended:false});
          if (err) return alert(err);
          $('#faleConoscoModal').modal('close');
          alert(`Mensagem enviada com sucesso!`);
          this._mensagem.value = '';
        });
    }

    _handleLogout() {
        logout();
    }

  render() {
    return (
    <aside className="sidebar-left">
        <ul className="side-nav fixed clearfix left" id="nav-mobile">
            <li>
                { (isLoggedIn() ) && this.state.user && this.state.signup_profile == false ? (
                <ul className="collapsible" data-collapsible="accordion">
                    <li>
                        <Link to='/mensagens' style={{ display: 'flex', alignItems: 'center' }}>
                            <span style={{ position: 'relative', display: 'inline-flex', marginRight: '8px' }}>
                                <i className="action-out material-icons blue-text darken-2" style={{ margin: 0 }}>
                                    mail_outline
                                </i>
                                {this.state.unreadCount > 0 && (
                                    <span style={{
                                        position: 'absolute',
                                        top: '2px',
                                        right: '-10px',
                                        backgroundColor: '#F44336',
                                        color: 'white',
                                        minWidth: '18px',
                                        height: '18px',
                                        borderRadius: '50%',
                                        fontSize: '10px',
                                        fontWeight: 'bold',
                                        display: 'flex',
                                        alignItems: 'center',
                                        justifyContent: 'center',
                                        zIndex: '10',
                                        border: '1px solid white',
                                        boxShadow: '0 2px 4px rgba(0,0,0,0.2)'
                                    }}>
                                        {this.state.unreadCount}
                                    </span>
                                )}
                            </span>
                            Mensagens
                        </Link>
                    </li>
                    <li>
                        <Link to='/search'>
                            <i className="action-out icon-search-8 blue-text darken-2"></i>Buscar
                        </Link>
                    </li>
                    <li>
                        <a href='https://votenet.net/processos'>
                            <i className="action-out icon-edit-2 blue-text darken-2"></i>Processos
                        </a>
                    </li>
                    <li>
                        <a href='http://www.votenet.net/'>
                            <i className="action-out icon-home-2 blue-text darken-2"></i>Institucional
                        </a>
                    </li>
                    <li>
                        <a href='/avaliacoes'>
                            <img style={{width:'8%', marginRight:'32px'}} src="https://votenet-django.s3.amazonaws.com/static/images/icon_avaliacoes.png" alt="Avaliações" />
                            Avaliações
                        </a>
                    </li>
                    <li>
                        <a href='/acompanha'>
                            <img style={{width:'8%', marginRight:'32px'}} src="https://votenet-django.s3.amazonaws.com/static/images/icon_acompanha.png" alt="Eleitos" />
                            Eleitos
                        </a>
                    </li>
                    <li>
                        <a href='/distritos'>
                            <img style={{width:'8%', marginRight:'32px'}} src="https://votenet-django.s3.amazonaws.com/static/images/icon_comunidade.png" alt="Informação Distrital" />
                            Informação Distrital
                        </a>
                    </li>
                    <li>
                        <a href='/interacoes'>
                            <img style={{width:'8%', marginRight:'32px'}} src="https://votenet-django.s3.amazonaws.com/static/images/icon_comunidade.png" alt="Interações" />
                            Interações
                        </a>
                    </li>
                    <li>
                        <a href='/versoes'>
                            <img style={{width:'8%', marginRight:'32px'}} src="https://votenet-django.s3.amazonaws.com/static/images/icon_changelog.png" alt="Versões" />
                            Versões
                        </a>
                    </li>
                    <li>
                        <a target="_blank" href='https://www.votenet.net/funcionalidades/' rel="noopener noreferrer">
                            <img style={{width:'8%', marginRight:'32px'}} src="https://votenet-django.s3.amazonaws.com/static/images/icon_funcionalidades.png" alt="Funcionalidades" />
                            Funcionalidades
                        </a>
                    </li>
                    <li>
                        <Link to="/litigation">
                            <i className="action-out icon-edit-2 blue-text darken-2"></i>
                            Eleição
                        </Link>
                    </li>
                    <li>
                        <a onClick={this.showFaleConoscoModal} href='#!' >
                            <img style={{width:'8%', marginRight:'32px'}} src="https://votenet-django.s3.amazonaws.com/static/images/icon_funcionalidades.png" alt="Fale Conosco" />
                            Fale conosco
                        </a>
                        <Modal id='faleConoscoModal'
                            header=''
                            modalOptions={{ dismissible: false}}
                            actions={
                                <Button waves='light' flat className="btn waves-light green darken-2 white-text modal-action modal-close">Fechar</Button>
                            }
                        >
                            <form role="signup-form" onSubmit={this._handleFaleConoscoSubmit}>
                                <div className="form">
                                    <div className="form-group">
                                        <label>Mensagem</label>
                                        <input
                                            type="text"
                                            ref={c => { this._mensagem = c; }}
                                            placeholder="Digite aqui sua mensagem"
                                            className="input input-text"
                                            required
                                        />
                                    </div>
                                    <div>
                                        <button type="submit" className="btn waves-light light-blue darken-2 white-text margin-left-10">
                                            Enviar mensagem
                                        </button>
                                    </div>               
                                </div>
                            </form>
                        </Modal>
                    </li>
                    <li>
                        <a onClick={this._handleLogout} style={{cursor: 'pointer'}}>                           
                            <i className="action-out icon-logout-2 red-text darken-1"></i>
                            Sair
                        </a>
                    </li>         
                </ul>
                ) : (
                    <div>
                        <a href='http://www.votenet.net/' className='notif-btn waves-effect waves-light'>
                            <i className="action-out icon-home-2 blue-text darken-2"></i>Institucional
                        </a>
                        <style>
                            {`
                                .notif-btn i {
                                    vertical-align: middle;
                                    display: inline-block;
                                    margin-right: 5px;
                                }
                                .notif-btn {
                                    display: inline-flex;
                                    align-items: center;
                                }
                            `}
                        </style>
                    </div>
                )}   
            </li>
        </ul>
    </aside>   
    );
  }
}

export default SideBar;

CLIQUE AQUI PARA OUVIR O AUDIO


O Mistério do Botão "Sair": 4 Lições Cruciais sobre Consistência em Apps Mobile

1. Introdução: O Hook do Usuário "Preso"

Imagine a frustração de um usuário da versão Beta v2.28m do app VoteNet. Após concluir suas interações, ele clica no  hamburger button  para encerrar a sessão, mas encontra-se em um beco sem saída digital: a opção "Sair" simplesmente não aparece. O usuário está, efetivamente, "preso".Este cenário, identificado em fase de homologação, não é um mero deslize estético. Como especialista, vejo aqui uma falha crítica de lógica e processo. O botão de logout, posicionado estrategicamente ao final da lista vertical do SideBar.jsx, tornou-se inacessível porque um erro de renderização anterior interrompeu o processamento do componente. O que parece um "bug visual" é, na verdade, um sintoma de um sistema quebrado por falta de rigor técnico.

2. A Armadilha do Teste "Desktop-First"

A investigação técnica revelou uma assimetria perigosa: a nova funcionalidade de "Mensagens" funcionava perfeitamente no Header.jsx (utilizado no Desktop), mas "quebrou" o SideBar.jsx (Mobile). Ao aplicar o Diagrama de Ishikawa, o pilar de  Mão de Obra  tornou-se o culpado evidente. O desenvolvedor, focado em resolver os desafios da interface horizontal, negligenciou a paridade com a versão mobile."O desenvolvedor esqueceu que faltavam os testes no celular... o foco foi direcionado, totalmente, para o comportamento no Header.jsx."Testar apenas em uma resolução é um risco crítico. No VoteNet, a entrega foi considerada concluída assim que os erros do Desktop foram sanados, ignorando que o código mobile, embora compartilhe a lógica de negócio, reside em arquivos distintos que exigem validação individual e específica.

3. Simetria é Mais que Estética: O Caso do Badge de Mensagens

A falha técnica central reside na ausência de sincronia entre os componentes. Enquanto o Desktop exibia o  badge  de notificações (o círculo vermelho), o mobile apresentava uma tag de link estática e incompleta. A ausência de variáveis cruciais e a falta de importação de utilitários como o ajax e o getAccessToken no SideBar.jsx causaram um erro de referência.Como o botão "Sair" é o último item da lista, a falha na lógica de renderização do item "Mensagens" (posicionado acima) impediu que o React terminasse de "desenhar" o restante do menu. Para o usuário, a experiência é fragmentada; para a engenharia, é um erro de integridade. A correção exige a implementação do estado unreadCount e a configuração de um ouvinte global (window.addEventListener('messageRead', ...)) no método componentDidMount, garantindo que o estado de mensagens não lidas seja consistente em qualquer lugar que o usuário acesse.

4. MASP e 5W2H: A Ciência por Trás da Correção

Tratar um bug de forma profissional exige fugir da "tentativa e erro". Utilizamos o MASP (Método de Análise e Solução de Problemas) para encontrar a causa raiz, desdobrando-a em um plano de ação 5W2H rigoroso para o SideBar.jsx:

  • O que (What):  Sincronizar a opção "Mensagens" no mobile com a lógica do desktop e corrigir importações ausentes.

  • Por que (Why):  Padronizar a UI/UX e evitar que falhas de renderização bloqueiem o acesso à opção "Sair".

  • Quem (Who):  Desenvolvedor Frontend.

  • Como (How):  Importar os módulos ajax e getAccessToken, inicializar o estado unreadCount: 0, implementar o método _updateUnreadCount e configurar o  listener  global no ciclo de vida do componente.

  • Quanto custa (How Much):  Aproximadamente 1 hora de desenvolvimento mais testes focados em dispositivos móveis.Este rigor garante que a causa raiz — a falta de variáveis no escopo — seja eliminada, permitindo que a lista vertical seja renderizada até o fim.

5. O Desafio do Código Legado (React 15.6.1)

A análise da  Máquina  (infraestrutura) revelou um detalhe vital: o bug era invisível em ambiente de Desenvolvimento, mas manifestou-se em Homologação. Em sistemas legados operando com  React 15.6.1  e  Node 6.17.1 , a manutenção exige precisão cirúrgica.Neste ambiente, não temos o luxo de usar  Hooks  modernos como useLocation ou useEffect. A correção precisou ser feita via  Class Components , utilizando métodos de ciclo de vida tradicionais. Manter a compatibilidade estrita é fundamental; tentar forçar padrões modernos em uma estrutura antiga sem o devido suporte de bibliotecas poderia introduzir efeitos colaterais ainda mais graves na estabilidade do sistema.

6. Conclusão: O Próximo Passo para a Homologação

A correção definitiva do "mistério do botão sair" não termina com o ajuste do código. Ela estabelece uma nova diretriz: a homologação deve ser tão rigorosa quanto o desenvolvimento. O processo de  code review  agora deve validar obrigatoriamente se as alterações no Header.jsx foram espelhadas no SideBar.jsx.A qualidade de um app é definida pela sua consistência em todos os pontos de contato. Se uma informação (como um badge de mensagens) existe em uma plataforma, ela deve ser uma verdade universal em todo o ecossistema do software.Fica a provocação:  em seus últimos  commits , você garantiu que a sua correção "Desktop" não deixou seus usuários "Mobile" sem uma rota de saída?



Nenhum comentário:

Postar um comentário

Entendendo a Metáfora "Home Office é Moto Boy"

UCEM L-pII.269 A minha visão é dirigida para contemplar a face de Cristo. - Caso de uso da IA para estudar

  Lição 269  A minha visão é dirigida para contemplar a face de Cristo. Lição anterior Escolher outra lição Próxima lição Texto e audio da L...