Como evitamos o desenvolvimento orientado por IC…?


45

Estou trabalhando em um projeto de código aberto liderado por pesquisas, com muitos outros colaboradores regulares. Como o projeto agora é bastante grande, um consórcio (composto por dois funcionários em período integral e poucos membros) é responsável por manter o projeto, a integração contínua (IC) etc. Eles simplesmente não têm tempo para a integração de recursos externos. contribuições embora.

O projeto é composto de uma estrutura "básica", com cerca de meio milhão de linhas de código, um monte de "plugins" mantidos pelo consórcio e vários plugins externos, a maioria dos quais não somos ' nem mesmo ciente.

Atualmente, nosso IC constrói o núcleo e os plugins mantidos.

Um dos grandes problemas que enfrentamos é que a maioria dos colaboradores (e principalmente os ocasionais) não está construindo 90% dos plug-ins mantidos; portanto, quando propõem mudanças de refatoração no núcleo (o que atualmente acontece regularmente), eles verificaram se o código é compilado na máquina antes de fazer uma solicitação de recebimento no GitHub.

O código funciona, eles estão felizes e, em seguida, o IC termina a construção e os problemas começam: a compilação falhou em um plug-in mantido por consórcio, que o colaborador não construiu em sua máquina.

Esse plug-in pode ter dependências de bibliotecas de terceiros, como CUDA, por exemplo, e o usuário não deseja, não sabe como, ou simplesmente não pode, por razões de hardware, compilar esse plug-in quebrado.

Então, então - ou o PR permanece ad aeternam no limbo de PRs que nunca serão mesclados - Ou o colaborador cumprimenta a variável renomeada na fonte do plugin quebrado, altera o código, pressiona sua ramificação, aguarda por o IC para concluir a compilação, geralmente recebe mais erros e reitera o processo até que o IC esteja satisfeito - ou uma das duas permanentes já contratadas em excesso no consórcio dá uma mãozinha e tenta consertar o PR em sua máquina.

Nenhuma dessas opções é viável, mas simplesmente não sabemos como fazê-lo de maneira diferente. Você já foi confrontado com uma situação semelhante em seus projetos? E se sim, como você lidou com esse problema? Existe uma solução que não estou vendo aqui?


84
A regra mais importante de fornecer uma API de plug-in a um sistema é que ela seja mantida estável ou, pelo menos, compatível com versões anteriores. Alterações no núcleo sem intencionalmente alterações na API de plug-in nunca devem interromper a compilação de nenhum plug-in (pode acontecer que ela quebre a funcionalidade por acidente, mas não a compilação). Se uma simples mudança de um nome de variável dentro do núcleo pode levar a uma compilação quebrada de um plug-in , a separação entre plug-ins e núcleo parece estar completamente quebrada.
Doc Brown


1
@KevinKrumwiede: Tenho certeza que eles já sabem disso ;-) Se você teve incompatibilidades, tenho certeza de que eles mudaram a API intencionalmente.
Doc Brown

3
Eu reformularia a pergunta, pois ela é realmente enganosa. Algo como Como gerenciar PRs quando eles quebram nosso IC atual? capturar melhor sua situação, eu acho.
bracco23

2
Quão difícil / complexo é o seu processo de compilação / teste? Deve ser apenas uma questão de executar um único comando ou clicar em um único botão. Nesse ponto, torna-se razoável esperar que os usuários executem todos os testes antes de enviar um PR.
Alexander

Respostas:


68

Desenvolvimento orientado por CI está bom! Isso é muito melhor do que não executar testes e incluir código quebrado! No entanto, existem algumas coisas para tornar isso mais fácil para todos os envolvidos:

  • Defina expectativas: tenha documentação de contribuição que explique que o IC frequentemente encontra problemas adicionais e que esses problemas precisam ser corrigidos antes da mesclagem. Talvez explique que mudanças locais pequenas são mais propensas a funcionar bem - portanto, pode ser sensato dividir uma grande mudança em vários PRs.

  • Incentivar testes locais: facilite a configuração de um ambiente de teste para o seu sistema. Um script que verifica se todas as dependências foram instaladas? Um contêiner do Docker pronto para ser usado? Uma imagem de máquina virtual? Seu executor de testes possui mecanismos que permitem priorizar testes mais importantes?

  • Explique como usar o IC para si: Parte da frustração é que esse feedback só ocorre após o envio de um PR. Se os contribuintes configurarem o IC para seus próprios repositórios, eles receberão comentários anteriores - e produzirão menos notificações de IC para outras pessoas.

  • Resolva todos os PRs, de qualquer maneira: se algo não puder ser mesclado porque está quebrado e se não houver progresso no sentido de solucionar os problemas, basta fechá-lo. Esses PRs abertos abandonados apenas bagunçam tudo, e qualquer feedback é melhor do que simplesmente ignorar o problema. É possível expressar isso muito bem e deixar claro que é claro que você ficaria feliz em mesclar quando os problemas forem resolvidos. (veja também: A arte do fechamento de Jessie Frazelle , Melhores práticas para mantenedores: aprendendo a dizer não )

    Considere também tornar esses PRs abandonados detectáveis, para que alguém possa buscá-los. Isso pode até ser uma boa tarefa para novos colaboradores, se os problemas restantes forem mais mecânicos e não precisarem de profunda familiaridade com o sistema.

Para a perspectiva de longo prazo, essas alterações parecem quebrar a funcionalidade não relacionada com tanta frequência que podem significar que seu design atual é um pouco problemático. Por exemplo, as interfaces do plug-in encapsulam adequadamente os internos do seu núcleo? O C ++ facilita o vazamento acidental de detalhes de implementação, mas também possibilita a criação de abstrações fortes e muito difíceis de usar indevidamente. Você não pode mudar isso durante a noite, mas pode orientar a evolução a longo prazo do software em direção a uma arquitetura menos frágil.


13
"Esses PRs abertos e abandonados simplesmente bagunçam tudo" Eu gostaria que mais mantenedores tivessem essa atitude 😔
GammaGames

34

Construir um modelo de plug-in sustentável requer que sua estrutura principal exponha uma interface estável na qual os plug-ins possam confiar. A regra de ouro é que você pode introduzir novas interfaces ao longo do tempo, mas nunca pode modificar uma interface já publicada. Se você seguir esta regra, poderá refatorar a implementação da estrutura principal tudo o que quiser, sem medo de quebrar acidentalmente os plug-ins, seja um mantido por consórcio ou um externo.

Pelo que você descreveu, parece que você não tem uma interface bem definida e isso dificulta saber se uma alteração interromperá os plugins. Trabalhe para definir essa interface e torná-la explícita na sua base de código, para que os colaboradores saibam o que não devem modificar.


20
O IC deve ter testes automatizados. Se você deseja garantir que os plug-ins tenham a mesma interface, todos os plug-ins devem contribuir com testes que expressam a interface de que precisam. Venha dessa maneira e quando a interface mudar, o que acontecerá, você saberá quais plug-ins está quebrando. Faça esses testes para executar localmente e saberei o que estou quebrando antes de emitir o PR.
candied_orange

1
A definição de @lagarkane é mais uma questão de política do que técnica. Existem softwares que, como o seu, simplesmente abandonam o comportamento anterior em uma atualização. O Perl5 não é compatível com o Perl6, o Python2.7 não é totalmente compatível com o Python3.4 etc. Há também softwares que, aconteça o que acontecer, ainda suportam código antigo. Você ainda pode executar quase todo o código javascript escrito para o Netscape Navigator 4 em navegadores modernos. A linguagem de programação Tcl é compatível com backwords de volta à versão original, etc ...
slebetman 08/08

3
@lagarkane: A formação do consórcio foi um passo na direção certa, e se os membros do núcleo concentrarem sua energia na criação dessas interfaces, você poderá aproveitar o poder de futuros doutores e estagiários para manter seu projeto em andamento, minimizando quebras. :)
casablanca

4
@Fattie: Isso funciona para a Apple porque eles criam produtos bem-sucedidos voltados para o consumidor e os desenvolvedores são forçados a seguir em frente, se quiserem fazer parte dele. É improvável que esses desenvolvedores realmente gostem de fazer alterações, e definitivamente não é um bom modelo para um projeto de código aberto.
casablanca

1
@casablanca tanto a linhagem MacOS quanto o WindowsOS são extremamente bem-sucedidas. (Indiscutivelmente, os dois maiores produtos em termos de dólares na existência humana.) Ao longo das décadas, eles tiveram abordagens absolutamente opostas. Aparentemente, ambos foram bem sucedidos!
Fattie

8

Para ser honesto, não acho que você possa lidar com isso de uma maneira melhor - se as alterações resultarem na quebra de partes mantidas do seu projeto, o IC falhará.

O seu projeto tem contributing.mdalgo ou algo semelhante para ajudar novos e ocasionais colaboradores a preparar suas contribuições? Você tem uma lista clara, quais plug-ins fazem parte do núcleo e precisam permanecer compatíveis?

Se for difícil criar tudo em uma máquina devido a dependências, etc., você pode pensar em criar imagens de janela de encaixe prontas para uso como ambientes de construção para seus colaboradores usarem.


1
Obrigado pela resposta! Sim, temos diretrizes de contribuição disponíveis publicamente, mas ele não lista os plugins como você sugere, o que já seria uma boa ideia. Criar imagens de janela de encaixe já parece uma grande melhoria para o processo de contribuição atual! Obrigado pela contribuição
lagarkane

8

portanto, quando eles propõem mudanças de refatoração no núcleo (o que atualmente acontece regularmente), eles verificaram que o código é compilado em sua máquina antes de fazer uma solicitação pull no github.

Então, acho que é aqui que o estilo mais amplo de projetos de código aberto pode cair; a maioria dos projetos organizados centralmente desconfia da refatoração principal, especialmente quando ela cruza um limite da API. Se eles refatoram um limite da API, geralmente é um "big bang", onde todas as alterações são agendadas ao mesmo tempo com um incremento na versão principal da API, e a API antiga é mantida.

Eu proporia uma regra "todas as alterações na API devem ser planejadas com antecedência": se um PR vier fazer uma alteração incompatível com a API, de alguém que não tenha entrado em contato com os mantenedores para concordar com sua abordagem antecipadamente, simplesmente é fechado e o remetente aponta para a regra.

Você também precisará de versão explícita da API do plug-in. Isso permite que você desenvolva a v2 enquanto todos os plugins da v1 continuam a ser construídos e a trabalhar.

Também questionaria um pouco mais por que estão sendo feitas tantas refatorações principais e alterações de API. Eles são realmente necessários ou apenas pessoas impõem seu gosto pessoal ao projeto?


2

Parece que o processo de IC precisa ser mais rigoroso, mais abrangente e mais visível para os colaboradores antes que eles aumentem um PR. Como exemplo, o BitBucket possui um recurso de pipelines que permite isso, onde você fornece um arquivo que define no código o processo de criação do IC e, se falhar, a ramificação será impedida de ser mesclada.

Independentemente da tecnologia, o fornecimento automático de compilações quando um colaborador envia para uma filial fornecerá a eles feedback muito mais rápido sobre as dicas a serem observadas ao fazer alterações e levará a PRs que não precisam ser consertados após o fato.

Seria bom corrigir problemas de design, mas são ortogonais a esse problema.


2

O código funciona, eles estão felizes e, em seguida, o IC termina a construção e os problemas começam: a compilação falhou em um plug-in mantido por consórcio, que o colaborador não construiu em sua máquina.

Esse plug-in pode ter dependências de bibliotecas de terceiros, como CUDA, por exemplo, e o usuário não deseja, não sabe como, ou simplesmente não pode, por razões de hardware, compilar esse plug-in quebrado.

Sua solução é simples: reduza a barreira à contribuição .

A maneira mais simples de (1) acelerar o ciclo de edição-compilação-teste e (2) diferenças suaves do ambiente é fornecer servidores de construção :

  • Escolha máquinas robustas: 24, 48 ou 96 núcleos, 2 GB de RAM / núcleo, SSD, para acelerar a compilação.
  • Verifique se eles têm o hardware certo: FPGA, placa gráfica, o que for necessário.
  • Crie uma imagem do Docker com todas as bibliotecas de software necessárias pré-instaladas.

E, em seguida, abra esses servidores de construção para colaboradores. Eles devem poder fazer logon remotamente em uma imagem nova do Docker e editar-compilar-teste remotamente nesta máquina.

Então:

  • Eles não têm desculpa para não criar / testar os plugins mantidos: eles têm tudo disponível.
  • Eles não precisam esperar por um longo feedback com PRs controlados por IC: eles têm compilação incremental e capacidade de depurar (em vez de adivinhar).

Em geral, os servidores de compilação podem ser compartilhados entre vários colaboradores, no entanto, quando periféricos de hardware especiais estão envolvidos, pode ser necessário que um colaborador use esse periférico sozinho.


Fonte: trabalhando em software usando FPGAs, dado o preço dos animais e a variedade de modelos que precisamos, você não encontra cada modelo de FPGA instalado na máquina de todos os desenvolvedores.


1

Se contribuir com o núcleo sem alterar nenhum contrato pode quebrar o software dependente, sugere que:

  • Os contratos de suas interfaces podem ser ambíguos. Talvez a adição de atributos em suas funções e parâmetros de função ajude a expor restrições adicionais ao código do cliente para tornar os contratos mais claros. Ou, se você estiver aplicando alterações de quebra de contrato, talvez a adoção de versões semânticas possa ajudar.
  • Os testes de unidade não estão cobrindo o suficiente dos possíveis cenários de chamada.

Qualquer um dos problemas deve ser fácil de resolver, mas você menciona que a equipe principal pode não ter capacidade para fazê-lo. Uma opção seria pedir ajuda à comunidade para resolver o problema.


1

Ninguém mais parece ter levantado isso como uma solução potencial.

  • liste todos os plugins que você pode acessar.
  • execute todos os testes que esses plugins definem
  • registre todas as solicitações / respostas / interações entre o núcleo e todos os plugins
  • armazenar essas gravações, agora são testes de compatibilidade aproximados.

Ao desenvolver o núcleo, incentive os desenvolvedores a executar esses testes de compatibilidade. Se eles falharem, não faça o check-in.

Isso não garante 100% de compatibilidade, mas irá capturar muito mais problemas desde o início.

Um benefício secundário é que essas gravações podem destacar quais interfaces são usadas ativamente e quais recursos estão sendo usados ​​ativamente.


0

Estou tendo problemas para entender a situação como parece: o IC cria apenas uma ramificação?

Existe um motivo para você não criar mais de uma ramificação com o IC?

A solução mais simples para esse problema seria possibilitar a qualquer colaborador executar a criação do IC em sua ramificação de recursos .

Em seguida, você simplesmente exige uma compilação de IC bem-sucedida na ramificação de recursos para que a solicitação de recebimento dessa ramificação seja aceita.


Isso parece resumir a questão.
Fattie

1
A pergunta diz "Ou o colaborador [...] altera o código, pressiona sua ramificação, espera que o IC termine de compilar, geralmente recebe mais erros e reitera o processo até que o IC esteja feliz" - então eu acho que isso já é o caso, mas o problema é que é um pouco doloroso se desenvolver com um ciclo tão longo de edição e depuração.
npostavs

@npostavs Obrigado, acho que foi isso que eu perdi na primeira vez ou duas que li. Mesmo assim ... acho que não pareço o problema. Existem muitas dependências, elas não podem ser quebradas, portanto, um colaborador precisa permanecer compatível com todas elas. Essa é a natureza do grande software. Certamente, poderia ser feito um trabalho para tornar a construção mais rápida, talvez, mas por outro lado, que atalho poderia haver?
Kyralessa
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.