Um pouco de experiência aqui - somos uma pequena equipe (de 5) de desenvolvedores da RAD responsáveis pelo desenvolvimento interno de software em uma grande empresa que não é de software. "Software interno" varia de um aplicativo .NET para desktop que usa o servidor MSSQL como back-end para scripts Python executados em segundo plano para documentos e modelos do MS Word - um zoológico de tecnologias.
Toda a equipe é formada por pessoas versáteis, capazes de obter os requisitos dos usuários, codificá-los, testá-los e implantar na produção. Uma vez que o software em produção está sendo tratado por outra equipe, mas geralmente é fácil intervir se algo der errado.
Tudo parece bom até agora, mas há um problema - sendo uma equipe da RAD que temos que liberar frequentemente, e não há um dia sem lançarmos novas versões de um ou dois aplicativos (ou pode ser um script, documento atualizado do word , Aplicativo de console C ++ etc.) na produção. Fazemos um teste de desenvolvimento e também envolvemos usuários finais, permitindo que eles executem o software no ambiente UAT ...
... mas os bugs estão chegando à produção de qualquer maneira. Os usuários entendem que esses bugs e a instabilidade ocasional são o preço que estão pagando para obter o que desejam rapidamente, mas, ao mesmo tempo, nos fez pensar - talvez possamos melhorar nosso desenvolvimento ou práticas de lançamento para melhorar a estabilidade do software e reduza o número de erros que apresentamos ao adicionar uma nova funcionalidade.
A coisa boa - nós realmente não temos muitos processos em primeiro lugar, por isso deve ser fácil começar a melhorar, a coisa ruim - sendo uma equipe pequena da RAD, não temos muito tempo e recursos para implementar algo grande, mas estivemos pensando nas seguintes iniciativas e gostaríamos de receber comentários, dicas, sugestões e sugestões.
Atualmente, alguns dos aplicativos estão sendo lançados na produção logo após o teste do desenvolvedor, ignorando o teste de aceitação do usuário. Essa prática deve ser descontinuada e até uma pequena alteração deve ser testada por um usuário final. Cada aplicativo terá um beta-tester dedicado selecionado entre os usuários finais. Somente após a aprovação do novo beta-tester, o novo release é promovido do ambiente de teste para a produção.
Não realizamos revisões de código - mas começaremos a fazer revisões de código antes que um de nós faça check-in no conjunto de alterações. Eu também estava pensando em uma "revisão de lançamento" - basicamente um dos desenvolvedores tem que se sentar ao lado do outro, observando-o fazendo o lançamento do software (copiar binários, atualizar configurações, adicionar nova tabela ao banco de dados etc.) - geralmente apenas demora de 5 a 10 minutos para não demorar muito tempo na "revisão de lançamento".
Como diminuir o tempo de reversão quando uma nova versão é comprovadamente buggy o suficiente para sair da produção e ser substituída por uma boa versão anterior. Armazenamos um histórico de todos os lançamentos (como binários) para facilitar a volta de uma versão - e, embora seja rápido "sobrescrever binários recém-lançados com binários de versões anteriores", ainda é um processo manual suscetível a erros e exigente às vezes "e se a reversão falhar e tornar o sistema inutilizável em vez de com erros".
É aqui que ficamos sem nossas idéias e gostaríamos de receber seu feedback sobre elas e se você pudesse compartilhar alguns conselhos simples de aprimoramento do processo de liberação / desenvolvimento - isso seria incrível.
Respostas:
+1 para tocar em um ótimo assunto. Quando fazemos a linha de desenvolvimento "Liberar versões antecipadas com frequência", as coisas aumentam o ritmo real e, à medida que o momento aumenta, surgem muitos desses problemas (como você descreveu), dos quais, de outra forma, não estamos muito preparados para lidar. O pior medo é quando as pessoas veem a velocidade como inimiga do bom trabalho.
Tenho visto literatura muito limitada sobre isso, no entanto, é isso que praticamos que definitivamente ajuda:
1. Rastreamento eficaz de erros
Torne o rastreamento de erros mais eficaz - o que fazemos não apenas mantém uma lista de bugs e marcas de escala, mas, quando fechado, devemos definir algumas coisas como "os problemas foram reproduzíveis?", "Essa é uma solução permanente ou correção de trabalho? "," qual é a causa raiz "do problema? Isso permite o conhecimento do que aconteceu, quando esse bug foi visível da última vez. Essa é a chave para garantir que os bugs não se repitam com frequência.
2. Defina os pontos principais de
fallback Todos sabemos que os bugs chegarão; portanto, precisaremos fornecer um fallback eficaz que funcione com mais freqüência. Repetidamente, finalizamos (com uma proporção de cerca de 1 em cada 10 no nosso caso) uma versão mais comum que funciona em todos os lugares da maneira mais confiável. O número total de lançamentos pode ser grande, mas se algo der errado, os retornos são poucos e você não precisa mais voltar atrás. Uma das maneiras mais simples de conhecer o melhor retorno é ver qual versão mais antiga está em execução há mais tempo na produção sem muitos problemas.
3. Distinguir lançamentos de correções de risco arriscados e estáveis ou pequenos
Quando sabemos que temos uma alteração importante no algoritmo, é mais provável que os erros possam surgir em cenários que nem todos estão previstos. Onde há momentos em que os problemas são muito pequenos (ou bem compreendidos) e têm pouco risco. Fazer NÃO clube tão funcionalidade e erros simples na mesmos lançamentos. Sempre tenha um bug menor corrigido primeiro, que deve ser executado sempre que necessário. Faça lançamentos dedicados para conjuntos de recursos especiais, na melhor das hipóteses, você pode descontinuar esse recurso, mas todos os outros bugs importantes ainda estão disponíveis, corrigidos no lançamento anterior.
4. Ramificação para desenvolvimento significativo de recursos
Qualquer coisa que associe alterações que afetem o design deve ser feita separadamente em uma ramificação. Desenvolvimento maior não é concluído rapidamente em comparação com bugs menores. Se introduzirmos commits intermediários, onde o trabalho 'parcial' relacionado ao recurso que ainda não está em uso - é uma região potencial de introdução de erros; os bugs que não teriam surgido se o trabalho completo para o recurso fosse concluído atomicamente - portanto, são bugs que não teríamos que resolver se houvesse ramificações.
5. Planeje sempre a liberação baseada em tema
Muitas vezes, muitos bugs diferentes chegam de versões diferentes - mas é melhor organizar bugs (e recursos) que afetam módulos semelhantes, facilitando o trabalho repetido e minimizando o número de bugs originados nesse trabalho. Sempre prepare o roteiro de lançamento com bastante antecedência; os bugs continuam aparecendo - e isso ocorre em diferentes lançamentos de destino, para otimizar um bom grupo de bugs a serem disparados juntos em um bom lançamento. Quando erros semelhantes são combinados, ele sempre fornece uma melhor visão sobre cenários contraditórios.
6. Estenda qualquer nova versão primeiro a alguns clientes
No nosso caso, vemos testá-la primeiro em alguns sites e todos os outros sites aplicam uma versão somente quando houver uma demanda por ela. Muitas vezes, alguns usuários (ou a maioria) saltariam apenas de uma versão estável para outra versão estável.
7. Teste de regressão
Ao longo das linhas de bugs sendo coletados - crie um processo de regressão. Além disso, se possível, marque os bugs críticos e teste como sendo os mais importantes, que se tornam os critérios mínimos de qualificação a serem testados antes que um candidato a release se torne um release.
8. Pausar e refletir
Quando muitas coisas acontecem a toda velocidade, deve haver tempo para fazer algumas pausas - faça uma pausa e faça lançamentos que não são funcionalmente melhores. De fato, tem férias de lançamentos há algum tempo. (a duração é inversamente proporcional à frequência). Por exemplo, muitas vezes temos os chamados lançamentos de "limpeza" que não alcançam nada de novo do ponto de vista da funcionalidade - mas isso ajuda muito na manutenção do código. Muitos desses lançamentos são ótimos pontos de retorno que você quase nunca lembra da história anterior a isso.
9. Talvez o mais estranho que
eu ache difícil implementar com frequência, mas esse seja um bom truque. Troque o proprietário de determinados módulos. Quando as pessoas são solicitadas a fazer revisões de código, isso não resulta muito dessa prática. Mas quando você precisa lidar seriamente com esse novo código, quando você troca de autores, possíveis doenças "ruins" são notadas rapidamente muito antes de começarem a poluir o código. Obviamente, isso reduz a velocidade - mas se você fizer isso com frequência, as chances são de que as pessoas dominem várias partes do código e aprendam sobre todo o produto, o que é muito difícil de ensinar.
10. Por último, mas não menos importante,
Aprenda a voltar ao quadro branco com frequência. Quanto mais você repensar como se esse recurso fizesse parte do nosso design mais inicial, como pensaríamos no design naquele momento? Às vezes, o maior desafio do trabalho incremental é que estamos muito limitados pela ordem da funcionalidade que criamos primeiro e, muitas vezes, não podemos voltar ao básico. O truque é continuar vendo como generalizaríamos, em vez de acomodar esse novo recurso ou cenário. Isso exige que o design permaneça atual, e isso acontece apenas se voltarmos à prancheta com frequência. Além disso, à medida que os programadores de nova geração se juntam, eles se tornam parte do grupo de reflexão em vez de apenas colocar os patches.
EDITAR
11. Acompanhe as soluções alternativas e as lacunas de design.
Muitas vezes, estamos sob pressão de prazos para corrigir o erro e liberar na produção. No entanto, quando o bug está no nível do design, algumas coisas precisam mudar, mas muitas vezes as pessoas corrigem alguns atalhos para cumprir o prazo. Isto está bom . No entanto, à medida que várias soluções alternativas aumentam, o código se torna frágil. Mantenha um controle especial de quantas lacunas de design já ocorreram. Normalmente, quando você negocia os cronogramas com o gerente de projetos, é melhor fazê-lo se comprometer em entregar isso em atalho para economizar produção, mas também teremos cronograma e recursos para obter uma solução permanente.
fonte
Também trabalho em uma pequena equipe de desenvolvedores (apenas dois de nós) e tivemos problemas semelhantes que você mencionou. O principal problema para nós é que ambos tendemos a trabalhar em tarefas separadas e estava se tornando muito comum concluir uma tarefa / recurso, testá-lo (testado apenas pelo desenvolvedor) e lançar rapidamente. Isso resultou em várias versões pequenas, com os usuários relatando pequenos erros que deveriam ter sido detectados facilmente nos testes.
Para melhorar nosso processo, comecei apresentando um quadro Kanban . Para começar, o quadro era muito simples e tinha apenas algumas colunas (configuração usando um quadro branco, fichas de índice e ímãs coloridos):
Lista de pendências | Para fazer | Feito
No entanto, isso evoluiu rapidamente para refletir nosso processo real:
Lista de pendências | Desenvolvimento | Dev. Teste UAT Feito Liberado
Juntamente com o quadro, temos uma regra de que cada tarefa / recurso deve ser testado por outro desenvolvedor (assim como pelo desenvolvedor que implementou o recurso). Portanto, quando o cartão chegar à coluna 'Concluído', ele deverá ter sido testado por pelo menos 2 desenvolvedores e também por Testes de Aceitação do Usuário.
Existem muitos outros benefícios no uso do Kanban. Para nós, melhorou a comunicação e nos ajudou a compartilhar conhecimento, já que estamos envolvidos em algum grau em cada tarefa. Também melhorou nosso processo de lançamento, pois agora podemos ver exatamente quais tarefas / recursos estão prontos para lançamento / execução e, às vezes, podemos adiar a liberação se soubermos que outras tarefas serão realizadas em breve. Para pessoas de fora da equipe, o conselho também atua como uma referência rápida para ver quais tarefas agendamos, o trabalho atual em andamento e o que foi lançado recentemente.
Como um aparte, usamos ímãs coloridos (um por desenvolvedor) para sinalizar o cartão em que estamos trabalhando atualmente, mas outra opção é adicionar raias de natação (linhas), uma por desenvolvedor e colocar os cartões Kanban em raias de natação relevantes. Novamente, isso ajuda como uma referência rápida para ver no que cada desenvolvedor está trabalhando atualmente.
Outros links que achei úteis:
Kanban aplicado ao desenvolvimento de software: do Agile ao Lean
Um sistema Kanban para engenharia de software - Vídeo
Espero que o Kanban resolva o ponto 1. da sua pergunta. Em relação às revisões de código, fazemos isso no estágio de teste do desenvolvedor, para que quaisquer alterações necessárias após a revisão sejam testadas novamente antes de ir para o UAT. A reversão depende do seu ambiente, mas implantamos arquivos de aplicativos nos Servidores de Terminal usando arquivos em lotes que renomeiam os arquivos atuais e copiam novos arquivos de um servidor central e podemos reverter com bastante facilidade colocando o backup (arquivos anteriores) no local central e re-executando scripts.
fonte
Você já identificou que sabe que existem problemas com seus processos que afetam a qualidade do seu software e, embora essa pergunta provoque várias respostas, minha sugestão seria examinar o tópico de engenharia de software e tentar aprender o que desenvolvedores em geral estão se fazendo cada vez mais nessa área. Sugiro que você comece a ler alguns bons recursos para começar. Alguns que vêm à mente:
Para melhorar o COMO você trabalha, você precisa ter uma mente completamente aberta e estar disposto a sair bem da sua zona de conforto, a fim de aprender a melhorar as coisas que faz, sem se apegar a certos conceitos que você achar que são. mais confortável para se agarrar. Falando por experiência pessoal, essa é provavelmente a coisa mais difícil de se fazer ou incentivar nos outros.
A mudança é difícil na melhor das hipóteses, mesmo que você sinta que está buscando ativamente a mudança. Portanto, o melhor conselho que eu posso realmente dar é examinar as várias metodologias ágeis que foram desenvolvidas ao longo dos anos para se familiarizar com as práticas. que são considerados os mais importantes (por exemplo: teste de unidade, integração contínua, refatoração, etc ...) e, em seguida, escolha a metodologia que parecer mais próxima do que você e sua equipe se sentirão mais confortáveis. Depois de tomar sua decisão, ajuste as práticas e seu processo de desenvolvimento para que ele se adapte à maneira como sua equipe prefere trabalhar, tendo em mente aqueles princípios enxutos e como você deseja trabalhar para que sua equipe possa produzir o maior valor possível com o menos desperdício. Finalmente,
Se você acha que seus processos precisam apenas de ajustes, mas está preocupado com o fato de sua cadeia de ferramentas não estar de acordo com suas necessidades, talvez procure melhorias nesse local. No mínimo, um produto de integração de integração contínua (como Continuum, Cruise Control ou Hudson) e um sistema de rastreamento de problemas (como Jira ou Redmine) devem ser uma prioridade para ajudá-lo a automatizar alguns de seus processos de criação e lançamento, e para manter seus erros e solicitações de recursos em cheque.
A realidade é que, não importa o quão RAD sejam seus processos, se você não investir tempo para fazer as coisas "corretas" para sua equipe, seus problemas continuarão a crescer com o tempo e sua percepção do tempo disponível aumentará. encolher de acordo. Geralmente, grandes mudanças estão fora de questão quando sob pressão excessiva, mas tente dar um pouco de "espaço de manobra" para colocar sistemas em funcionamento para ajudá-lo a dar pequenos passos em direção à visão de uma metodologia ideal da sua equipe.
fonte
Sempre que ouço sobre defeitos, minhas primeiras perguntas são sobre onde os defeitos se originam e onde são detectados e removidos. Eficiência na remoção de defeitos é uma boa maneira de medir e rastrear isso. Ao saber de onde os defeitos se originam e trabalhar para melhorar os processos nessas fases, você pode reduzir o tempo e o custo de um projeto. É sabido que é mais barato corrigir os defeitos mais perto do ponto de injeção, portanto, quando você souber de onde vêm os defeitos, poderá ver as mudanças nas atividades para melhorar essas fases.
Depois de obter informações sobre a origem dos defeitos, você pode analisar exatamente quais técnicas e tecnologias deseja aplicar. Revisões de requisitos, design e código, testes automatizados, análise estática, integração contínua e testes orientados ao usuário mais abrangentes podem ser opções que você deve considerar, dependendo de quais fases geram defeitos.
Para expandir seu desejo de revisões de código, você também deve considerar diferentes níveis de revisões de código com base na prioridade e no risco de um módulo. Módulos de baixo risco e baixa prioridade podem não precisar de uma revisão de código, ou talvez apenas uma simples verificação de mesa, onde outro desenvolvedor apenas leia o código por conta própria e forneça comentários, funcionaria. Outras técnicas de revisão de código incluem programação em pares, orientações, críticas e inspeções com vários números de desenvolvedores.
Para fins de reversão, eu procuraria automatizar esse processo usando algum tipo de script para torná-lo mais rápido e menos propenso a erros. Em um mundo perfeito, eu gostaria de aumentar a qualidade dos produtos enviados, de modo que não seja necessário reverter, e você pode conseguir isso. Ter a capacidade, no entanto, pode ser uma boa ideia, mas torne-a o mais simples possível.
fonte
Como outros já apontaram, adicionar testes de regressão ajudará a evitar os mesmos defeitos que aparecerão no futuro. No entanto, se você encontrar novos defeitos, talvez seja hora de adicionar asserções (também conhecidas como contratos) ao código que especifica as pré-condições, pós-condições e invariantes das classes e métodos.
Por exemplo, se você tiver uma classe em que um método possa aceitar apenas números entre 10 e 25 (isso é chamado de pré-condição), adicione uma declaração assert no início do método. Quando essa afirmação falhar, o programa falhará imediatamente e você poderá seguir a cadeia de métodos que levou a essa falha.
Python, PHP e outras linguagens de programação são digitadas dinamicamente e não adicionam muitas condições aos métodos. Tudo o que é necessário para que algo funcione é que ele implemente um método específico. Eu suspeito que você precisa de mais condições em seus métodos. Você precisa definir e testar para garantir que um método possa realmente funcionar em seu ambiente.
Para programas C / C ++, descobri que adicionar asserções para testar a alocação de memória era muito útil para reduzir o número de vazamentos de memória no programa.
fonte