Na DoorDash, trabalhamos para implementar processos eficientes que possam mitigar conflitos comuns numa grande equipa de desenvolvimento iOS. Parte desses esforços envolve a utilização do XcodeGen, uma interface de linha de comandos (CLI), para reduzir os conflitos de fusão nas nossas várias equipas de iOS. Aqui vamos discutir a sua implementação para gerir os intrincados cenários empresariais e os exigentes requisitos da aplicação Dasher, que permite aos nossos condutores receber, recolher e entregar encomendas aos clientes de forma segura. Com várias configurações, dependências, mecanismos de localização, scripts de pré e pós-construção e uma equipa em constante expansão, existem riscos elevados de instabilidade da base de código e falta de escalabilidade no desenvolvimento de uma aplicação deste tipo. A resolução de conflitos comuns de mesclagem do xcodeproj pode ser demorada, mas é uma parte crítica da manutenção da velocidade do desenvolvedor e da garantia da operação tranquila de uma equipe grande e complexa.
Ao examinar o desenvolvimento da nossa aplicação de driver, podemos aprofundar os desafios de manter um projeto Xcode para uma grande equipa, incluindo as dores de crescimento que as grandes equipas enfrentam e as dificuldades de refactoring de grandes projectos. Demonstramos como o XcodeGen pode resolver estes problemas e ilustramos como as equipas DoorDash Consumer e Driver o utilizaram com sucesso para ultrapassar os desafios. Por fim, compartilhamos nossas percepções e experiências sobre como usar o XcodeGen em um projeto de grande escala.
Porque é que os conflitos de fusão do xcodeproj prejudicam a colaboração
O Xcode é uma ferramenta essencial para os programadores de iOS e é utilizado para várias tarefas, como escrever código, criar interfaces de utilizador e analisar as métricas da loja de aplicações, entre muitas outras coisas. Apesar da sua importância, a complexidade dos ficheiros xcodeproj pode dificultar a gestão eficaz dos projectos e garantir uma colaboração perfeita entre os membros da equipa. Um projeto pode consistir em qualquer coisa entre dez ficheiros e milhares de ficheiros, incluindo código-fonte, activos, scripts e ficheiros de texto simples. O Xcode organiza estes ficheiros numa ordem aparentemente arbitrária, que parece ser influenciada por quando e como os programadores os incorporam.
No Xcode, todos os códigos-fonte e recursos devem ser vinculados a um destino para fazer parte do produto final, o que significa que sempre que um arquivo é adicionado, o Xcode cria uma referência de arquivo para garantir sua inclusão no destino. Para manter tudo isto, o Xcode aloja o ficheiro pbxproj no projeto xcodeproj, que contém a estrutura completa do projeto. Para uma equipa pequena, as modificações contínuas do projeto Xcode podem não ser uma ocorrência comum. Desde que a equipe não esteja frequentemente mesclando código ou ajustando arquivos de projeto do Xcode, eles provavelmente não serão incomodados por problemas de conflito de mesclagem. No entanto, à medida que uma equipa cresce ou incorpora ferramentas de integração contínua, pode enfrentar um aumento significativo de conflitos de fusão, o que pode ter um impacto considerável na sua velocidade de desenvolvimento.
O arquivo pbxproj consiste em código gerado por máquina, que não pode ser facilmente revisado por humanos. A menos que haja um conflito de mesclagem, esse código raramente é examinado, o que significa que os desenvolvedores têm conhecimento e compreensão limitados sobre como o Xcode gerencia esses itens. Como resultado, os problemas podem surgir apenas quando uma equipa se expande, dificultando a eficiência e a eficácia.
Mas quando uma equipa móvel aumenta de escala, a eficiência e a velocidade tornam-se cruciais. O Xcode, particularmente o código gerado por máquina no ficheiro pbxproj, pode colocar desafios ao crescimento. Por ser complexo e difícil de rever por humanos, os programadores não sabem como o Xcode gere os ficheiros.
Esta complexidade aumenta à medida que vários programadores adicionam e removem ficheiros, actualizam referências de ficheiros e fazem alterações às definições de compilação, fases, configurações e esquemas. Em última análise, os frequentes conflitos de fusão atrasam o progresso da equipa, limitando a sua capacidade de escalar eficazmente.
Por conseguinte, é essencial dispor de um processo que possa gerir as referências e a estrutura dos ficheiros Xcode, bem como de um conjunto de ferramentas bem equipado que possa atenuar estes desafios e melhorar a capacidade da equipa para gerir ficheiros de projeto e executar tarefas de forma eficiente. Ao abordar a complexidade do ficheiro pbxproj e implementar um processo simplificado para gerir ficheiros Xcode, uma equipa móvel pode escalar eficazmente, evitar estrangulamentos e maximizar a sua eficiência e velocidade.
Qualquer pessoa que colabora frequentemente em projectos Xcode está provavelmente familiarizada com os desafios de resolver conflitos de fusão resultantes de ficheiros de projeto que são difíceis de ler. É difícil determinar uma resolução de conflito de mesclagem apropriada apenas olhando para o XML bruto, gerado automaticamente. Além disso, sem recompilar, não é claro se uma resolução foi bem-sucedida. A Figura 1 mostra um exemplo de alteração da estrutura do ficheiro e causa de conflitos:
Há três questões principais envolvidas na resolução de conflitos de fusão:
- Demorado e propenso a erros - A comparação cuidadosa de versões conflituosas de um ficheiro xcodeproj pode ser assustadora para projectos complexos ou de grandes dimensões. E, apesar dos melhores esforços, quaisquer erros ou conflitos negligenciados podem causar problemas que exigem tempo e esforço adicionais para serem corrigidos.
- Risco de perda de alterações - Se um conflito de fusão não for resolvido corretamente, existe o risco de algumas alterações se perderem ou serem substituídas. Isto pode ser particularmente problemático se as alterações perdidas forem críticas para a funcionalidade de um projeto.
- Potencial de corrupção do projeto - Em alguns casos, um conflito de fusão pode corromper o projeto de forma tão grave que não pode ser aberto no Xcode. O trabalho pode parar até que o projeto seja restaurado para uma versão anterior ou - na pior das hipóteses - o projeto pode ter de ser reiniciado a partir do zero.
Em geral, conflitos de mesclagem dentro de um arquivo xcodeproj podem apresentar desafios notáveis para desenvolvedores e equipes que trabalham em projetos Xcode. No entanto, ferramentas como o XcodeGen podem ajudar a contornar e resolver conflitos de mesclagem mais rapidamente, aumentando a consistência e a confiabilidade e minimizando os riscos.
Como lidar com a refacção de projectos
A multiplicidade de alvos, arquivos e dependências envolvidos na refatoração de projetos grandes e complexos pode ser um desafio. É difícil identificar e modificar partes relevantes do projeto sem quebrar ou afetar outras partes. Além disso, os projetos do Xcode geralmente dependem de bibliotecas ou estruturas externas, como o CocoaPods ou o Swift Package Manager (SPM). A refatoração pode exigir a atualização dessas dependências para garantir a compatibilidade com a estrutura atualizada do projeto.
A colaboração com outros membros da equipa também pode representar um desafio. A comunicação e a coordenação das alterações podem ser necessárias para evitar conflitos e garantir que todos trabalham com a mesma estrutura e configuração de projeto.
É essencial manter a compatibilidade com diferentes versões do Xcode e outras ferramentas ao refatorar um projeto Xcode existente. A compatibilidade com versões anteriores do Xcode ou outras ferramentas é frequentemente necessária para garantir que todos os membros da equipa possam continuar a construir e utilizar o projeto.
Na DoorDash, muitas vezes realizamos a refatoração de projetos por motivos semelhantes. Por exemplo, o Caviar da DoorDash é o maior mercado do mundo para restaurantes premium e entusiastas de comida, enquanto o mercado da DoorDash oferece uma variedade de comerciantes que atendem a todos e a todas as ocasiões. Embora estes produtos sirvam necessidades diferentes, as equipas de engenharia podem partilhar muitas ferramentas, tecnologias e bases de código.
Compreendemos que a duplicação de código conduz naturalmente à partilha de código e à criação de módulos partilhados. No entanto, dividir projetos monolíticos em módulos requer muita refatoração. É necessário mais do que um passo para criar cada módulo, por isso temos de utilizar precauções, ferramentas e processos adequados para manter o projeto estável enquanto continuamos a trabalhar para atingir os nossos objectivos.
Existem duas opções para refatorar um projeto:
- Abordagem descendente - Este método envolve a reconstrução do projeto a partir do zero. Embora a aprendizagem prévia e as ferramentas inteligentes possam ser utilizadas para tornar o processo mais seguro e mais rápido, é necessário comprometer-se com uma reescrita completa.
- Abordagem ascendente - Este método, que é geralmente considerado o mais prático e seguro, executa tudo como está, com alterações feitas de forma incremental.
Num artigo recente - Adotar o SwiftUI com uma abordagem de baixo para cima para minimizar o risco - discutimos estas duas abordagens em detalhe. Na nossa adaptação do SwiftUI e Combine, uma abordagem bottom-up provou ser muito mais eficaz na criação das bases, porque tivemos que passar por inúmeras reestruturações e modificações do projeto antes de finalmente alcançar a modularização.
Quando adotamos o SwiftUI no projeto DasherDriver, sabíamos que precisaríamos mover arquivos e diretórios, alterar alvos e realizar várias outras tarefas relacionadas ao projeto Xcode à medida que fizéssemos grandes alterações no código. Todas essas tarefas no início do processo de reestruturação exigem uma quantidade significativa de tempo. Além disso, como era um processo contínuo que envolvia a iteração constante do trabalho de engenharia e do trabalho do produto, havia um risco crescente de conflitos de fusão à medida que a equipa maior instituía continuamente alterações de código e alterava a estrutura do projeto. Para evitar abrandamentos, sabíamos que tínhamos de evitar conflitos a todo o custo.
Dada a dificuldade que a nossa equipa tinha em ler rapidamente os ficheiros de projeto do Xcode, optámos por resolver inicialmente os problemas de conflitos de fusão. Reconhecemos que confiar apenas no Xcode IDE não seria suficiente para manter a eficiência. Ao empregar o XcodeGen CLI, conseguimos avançar na fase de refatoração sem encontrar obstáculos significativos.
Mantenha-se informado com as actualizações semanais
Subscreva o nosso blogue de Engenharia para receber actualizações regulares sobre todos os projectos mais interessantes em que a nossa equipa está a trabalhar
Please enter a valid email address.
Obrigado por subscrever!
Como é que o XcodeGen ajuda a reduzir os erros de fusão
A capacidade do XcodeGen de definir as especificações do projeto em arquivos YAML ou JSON oferece vários benefícios em relação à criação e manutenção manual do projeto. Ele fornece consistência e facilidade de manutenção, o que é crucial em projetos de grande escala. Ao definir a estrutura e as configurações de um projeto em um único arquivo YAML, é possível garantir que os projetos tenham uma estrutura e configuração consistentes. Isto facilita a manutenção e a atualização dos projectos ao longo do tempo. As alterações podem ser feitas simplesmente editando o arquivo YAML e depois regenerando o projeto Xcode para aplicar essas alterações.
O XcodeGen também melhora a colaboração. Com a especificação do projeto armazenada num ficheiro de texto, pode ser facilmente versionada e partilhada com os membros da equipa. Isso permite que todos trabalhem na mesma especificação de projeto e garante a mesma configuração de projeto em toda a linha. Os membros da equipa podem colaborar de forma mais eficiente, evitar conflitos e saber que as alterações são aplicadas corretamente.
O XcodeGen permite flexibilidade e personalização no desenvolvimento de projetos. Com esta ferramenta, os utilizadores podem personalizar diferentes aspectos do seu projeto - incluindo definições de construção, alvos e esquemas - para se adequarem às suas necessidades e requisitos específicos. Esse nível de personalização é particularmente útil para projetos que exigem configurações exclusivas ou têm requisitos específicos que não podem ser alcançados por meio de configurações padrão. O XcodeGen também se integra facilmente a outras ferramentas e bibliotecas, como CocoaPods, SPM e Fastlane, facilitando o gerenciamento de dependências externas em projetos e automatizando tarefas comuns, como a criação e a implantação de aplicativos.
Para compreender o poder do XcodeGen, precisamos de compreender a confusão do .pbxproj. O Xcode gera um GUID para cada referência de arquivo e usa essa referência em todos os outros lugares dentro do .pbxproj. Para mostrar isso, criamos um arquivo dentro de um projeto de exemplo chamado "MyExampleApp.swift". Observe no segmento de código a seguir que o Xcode atribuiu um GUID para ele. Sempre que no projeto usarmos ou nos referirmos a esse arquivo, o Xcode usará o GUID para vincular corretamente.
Vamos analisar alguns cenários do mundo real. O projeto de teste tem a estrutura apresentada na Figura 2:
O programador A decidiu criar outro grupo chamado "Código" e moveu todos os ficheiros Swift para esse grupo. Após essa operação, a estrutura do projeto passa a ter o aspeto mostrado na Figura 3:
Devido a essa operação do Xcode, o arquivo .pbxproj é alterado. As alterações no nosso ficheiro git são apresentadas na Figura 4:
Essas são mudanças substanciais, todas ilegíveis, para apenas uma operação do Xcode IDE. Agora as coisas ficam realmente feias quando o Desenvolvedor B adiciona um arquivo de código na hierarquia raiz mesmo antes das alterações iniciais entrarem no branch principal. O layout do projeto Xcode do ramo local do Desenvolvedor B aparece como mostrado na Figura 5:
Agora está claro onde o conflito de mesclagem está começando. Qualquer conflito de mesclagem de projetos do Xcode requer um processo manual e demorado. Pior ainda, há uma excelente chance de que algo mais tenha sido inadvertidamente bagunçado e que não se tornará conhecido até muito mais tarde.
Em vez disso, vamos gerenciar o mesmo tipo de situação usando a magia do XcodeGen. Depois de seguir as instruções daqui, temos project.yml no nosso diretório de projectos. Preenchemos o YML da seguinte forma:
A parte surpreendente do ficheiro YAML é que pode ser lido a partir do topo para compreender imediatamente o objetivo:
- Definimos o nome do projeto
- Corrigimos algumas opções globais mais importantes
- Em seguida, definimos um destino com um diretório de código-fonte
- E acabámos!
Depois de gerar o projeto utilizando o comando XcodeGen, o Xcode IDE corresponderá exatamente ao que descrevemos no ficheiro YML. Mas alcançámos marcos técnicos importantes:
- Somos nós, e não o IDE, que controlamos a estrutura do projeto
- Nós controlamos as definições e estas serão reflectidas em conformidade
- Acabámos de evitar um conflito de fusão de ficheiros de projeto
O instantâneo do IDE do projeto aparece como se mostra na Figura 6:
E se, apesar de nossos esforços, um conflito de mesclagem ainda precisar ser resolvido? Podemos simplesmente descartar o .pbxproj de entrada e executar o XcodeGen para gerar o arquivo novamente - e pronto. Em essência, minutos ou horas de resolução complexa de conflitos de mesclagem podem ser reduzidos a menos de um minuto.
Desvantagens do processo XcodeGen
É sempre perturbador fazer quaisquer alterações num projeto maior e com uma equipa maior. Consequentemente, temos de nos certificar de que temos uma forma de integrar toda a gente para que se possa adaptar rapidamente às mudanças. No nosso caso, fizemos uma alteração no nosso fluxo de trabalho que exigiu, durante algum tempo, a realização frequente de sessões individuais. Também criámos um canal de apoio dedicado para garantir que todos estavam na mesma página. Esta fase, no entanto, foi curta e rapidamente evoluiu para um processo diário de rotina.
Existem algumas desvantagens potenciais na utilização do XcodeGen em alguns casos:
- Falta de suporte para recursos do Xcode - O XcodeGen não suporta todos os recursos e configurações disponíveis no Xcode. Por exemplo, o XcodeGen não pode criar trechos de código personalizados ou gerenciar arquivos de localização. Se um projeto depender destas funcionalidades, o Xcode poderá ainda ser necessário para algumas tarefas.
- Documentação limitada e suporte da comunidade - Como o XcodeGen é uma ferramenta relativamente nova, pode não haver tanta documentação e suporte da comunidade disponíveis quanto para outras ferramentas. Isto pode dificultar a iniciação de novos utilizadores e a resolução de quaisquer problemas que encontrem.
- Potencial para conflitos com o Xcode - Como o XcodeGen gera um projeto Xcode a partir de um arquivo YAML, o projeto gerado pode nem sempre estar em sincronia com o conteúdo real do projeto no disco. Se forem feitas alterações fora do XcodeGen, existe o risco de essas alterações serem substituídas ou perdidas quando o projeto for gerado novamente.
Embora o XcodeGen ofereça muitos benefícios, pode não ser a solução certa para todos os projectos ou equipas de desenvolvimento. É importante avaliar cuidadosamente as necessidades e os requisitos antes de decidir se deve utilizar o XcodeGen no seu fluxo de trabalho.
Impacto da utilização do XcodeGen: Mais velocidade
Aqui estão os principais problemas que o XcodeGen pode ajudar a resolver:
- Reduzir os conflitos de fusão de projectos para quase zero - Depois de aplicar o XcodeGen às aplicações DoorDash e Dasher, a nossa equipa de mais de 100 engenheiros não sofreu conflitos de fusão de projectos! A integração do XcodeGen foi muito fácil para a nossa equipa e começou a compensar quase imediatamente. Mesmo tendo em conta o esforço investido na integração do XcodeGen, já poupámos tempo significativo e continuaremos a fazê-lo no futuro.
- Modularidade e partilha de código - O Caviar e o DoorDash são construídos a partir do mesmo projeto em dois alvos diferentes, utilizando o XcodeGen e um alvo XcodeGen com modelos. Sem uma ferramenta baseada em CLI, haveria um risco enorme para ambos os projectos. O XcodeGen imunizou-nos contra esse risco.
- Migrando de Cocoapods para SPM - Em uma época, todos os projetos do Xcode eram um monte de emaranhados de Pods. Os Pods tinham muitas limitações técnicas e então a Apple trouxe o SPM para gerenciar o empacotamento e a modularidade. Migrar código de Pods para SPM era um processo assustador e o XcodeGen foi nosso salvador também.
Ao utilizar o XcodeGen para gerar projectos Xcode, garantimos que os nossos projectos têm uma estrutura e configuração consistentes. Este processo facilita a manutenção e a atualização de projectos ao longo do tempo, reduzindo o risco de erros e inconsistências. Uma vez que a especificação do projeto é armazenada num ficheiro de texto, criámos modelos para reutilizar nos nossos diferentes projectos. A utilização do XcodeGen permite que todos trabalhem com as mesmas especificações e configurações de projeto. Permitiu-nos personalizar vários aspectos do nosso projeto, tais como as definições de construção, alvos e esquemas, ao mesmo tempo que nos deu a flexibilidade para adaptar os nossos projectos a necessidades e requisitos específicos.
Conclusão
A DoorDash enfrentou desafios com equipas de escalonamento e resolução de conflitos de fusão em projectos Xcode. Ao adotar uma ferramenta que permitia configurações de projeto mais fáceis de manter e de ler, o XcodeGen ajudou a resolver conflitos de fusão mais facilmente e simplificou os processos de desenvolvimento. O XcodeGen é particularmente valioso para equipas maiores que lidam com estruturas de projeto complexas. Embora o XcodeGen possa não ser necessário para equipas mais pequenas, tornou-se uma parte importante do conjunto de ferramentas da DoorDash até (e a menos que) a Apple forneça uma solução integrada no Xcode. Em suma, se estiver a enfrentar desafios semelhantes no desenvolvimento do seu projeto Xcode, vale a pena avaliar se o XcodeGen pode ser a sua solução.