Cristian Fernandes en Atendimento de suporte técnico remoto, Java Brasil, Programadores Atendente • Carrefour 13/11/2016 · 10 min de lectura · +700

Design Patterns

O que são Design Patterns?

Design Patterns

Design patterns (padrões de projeto) surgiram com a motivação de ajudar a solucionar problemas que ocorrem frequentemente, e, se usados com bom senso, podem se tornar ferramentas poderosas para qualquer desenvolvedor de software, uma vez que já foram testadas, utilizadas e aprimoradas a partir da experiência e conhecimento de outros programadores.

http://buff.ly/2fNg0lr


Sobre o Desenvolvimento Orientado por Documentação

Design Patterns

Eu adoro projetar e desenvolver APIs. Na construção de uma API grande, os processos de design e desenvolvimento exigem atenção igual. O problema é que as abordagens populares de desenvolvimento não enfatizam o processo de projeto. Você pode estar familiarizado com os conceitos de desenvolvimento orientado por teste e desenvolvimento orientado por comportamento, mas vamos falar sobre o conceito menos conhecido de "desenvolvimento orientado por documentação".

Tom Preston-Werner, co-fundador do GitHub, escreveu um blog fantástico sobre os méritos do desenvolvimento orientado a leitura. Esta abordagem é a sua melhor aposta para 90% dos projetos.

Então, quais projetos compõem os outros 10%?

APIs

Projetando uma API é um caso especial, porque você está muitas vezes projetando para desconhecidos. Você pode não saber:

  • Que tipos de clientes consumirão a API ou suas preferências individuais.

  • O fluxo que qualquer consumidor determinado tomará através de seus pontos de extremidade da API.

  • Informações que serão mais valiosas para o consumidor.

Com todas essas incógnitas, a melhor coisa que você pode fazer para si mesmo e seus consumidores é escrever a sua documentação em primeiro lugar, tão visivelmente quanto possível. O objetivo é reunir insight e recomendações de seus consumidores cedo e durante todo o processo de design, transformando suas incógnitas em conhecidos. As alterações são mais fáceis e mais rápidas na documentação do que no código.

O fluxo de trabalho é:

  • Gravar ou atualizar a documentação.

  • Escreva um teste de falha de acordo com essa documentação (red).

  • Escrever código para passar no teste de falha (green).

  • Refator.

Nossa Experiência

Na Ideia Coletiva, o desenvolvimento orientado a documentação tem sido maravilhoso para escrever APIs. Vimos vários benefícios:

Velocidade

Muitas vezes, vamos escrever uma API para um consumidor dedicado, como um aplicativo iOS. A documentação de escrita fornece primeiro um espaço no qual as equipes do servidor e do cliente podem colaborar. A documentação também fornece à equipe do cliente algo para trabalhar antes que a equipe do servidor tenha implementado os pontos de extremidade documentados. Isso evita o retrabalho e paraleliza os esforços das equipes do servidor e do cliente.

Consistência

Documentação precisa e atualizada é importante para uma API, especialmente uma API pública. Ao escrever ou atualizar a documentação é uma primeira etapa necessária do seu processo de desenvolvimento, você garante que sua documentação seja consistente com seu código.

Transparência

O desenvolvimento orientado por documentação eleva a documentação de escrita ao mesmo nível de importância que escreve o código. Como resultado, alterações de documentação também são incluídas no processo de revisão por pares. Isso dá ao usuário a visão sobre o "porquê" por trás de um conjunto de alterações de código. Revisão de código é muito mais eficaz quando o revisor tem essa compreensão da finalidade.

Se você está escrevendo uma API, dê um desenvolvimento orientado a leitura ou orientado a documentação e nos informe sobre sua experiência.

Desenvolvimento com Readme Driven

Eu ouço muita conversa nestes dias sobre TDD e BDD e programação extrema e SCRUM e reuniões de pé e todos os tipos de metodologias e técnicas para desenvolver um melhor software, mas é tudo irrelevante a menos que o software que estamos construindo atende às necessidades daqueles que Estão usando. Deixe-me colocar isso de outra maneira. Uma implementação perfeita da especificação errada é inútil. Pelo mesmo princípio uma biblioteca belamente crafted com nenhuma documentação é também maldita quase worthless. Se o seu software resolve o problema errado ou ninguém pode descobrir como usá-lo, há algo muito ruim acontecendo.

Bem. Então, como podemos resolver esse problema? É mais fácil do que você pensa, e é importante o suficiente para justificar seu próprio parágrafo.

Escreva seu Readme primeiro.

Primeiro. Como em, antes de escrever qualquer código ou testes ou comportamentos ou histórias ou qualquer coisa. Eu sei, eu sei, nós somos programadores, maldição, não escritores de tecnologia! Mas é aí que você está errado. Escrever um Readme é absolutamente essencial para escrever bom software. Até você ter escrito sobre o seu software, você não tem idéia do que você vai ser codificação. Entre a grande folga contra o projeto da cachoeira ea aceitação suprema do desenvolvimento ágil, algo foi perdido. Não me interpretem mal, design de cachoeira leva as coisas muito longe. Sistemas enormes especificados em detalhes minuciosos acabam sendo os sistemas ERRADOS especificados em detalhes minuciosos. Estávamos certos em derrubá-la. Mas o que tomou o seu lugar é muito longe na outra direção. Agora temos projetos com documentação curta, mal escrita ou totalmente ausente. Alguns projetos não têm sequer um Readme!

Isso não é aceitável. Deve haver algum meio termo entre resmas de especificações técnicas e nenhuma especificação em tudo. E de fato há. Esse ponto intermediário é o humilde Readme.

É importante distinguir o Readme Driven Development do Documentation Driven Development. RDD poderia ser considerado um subconjunto ou versão limitada de DDD. Ao restringir a documentação de projeto a um único arquivo que se destina a ser lido como uma introdução ao seu software, o RDD mantém você protegido contra a síndrome de DDD-virou-cachoeira punindo você por especificações longas ou excessivamente precisas. Ao mesmo tempo, recompensa você por manter as bibliotecas pequenas e modularizadas. Esses reforços simples ir um longo caminho para a condução do seu projeto na direção certa, sem um monte de processo para garantir que você faça a coisa certa.

  • Ao escrever o seu Readme primeiro você se dá algumas vantagens bastante significativas:

    • Mais importante ainda, você está dando a si mesmo a chance de pensar no projeto sem a sobrecarga de ter que alterar o código toda vez que você mudar de idéia sobre como algo deve ser organizado ou o que deve ser incluído na API pública. Lembre-se que o sentimento quando você começou a escrever testes de código automatizado e percebeu que você pegou todos os tipos de erros que teria de outra forma snuck em seu codebase? Essa é exatamente a mesma sensação que você terá se você escrever o Readme para seu projeto antes de escrever o código real.
    • Como um subproduto de escrever um Readme, a fim de saber o que você precisa para implementar, você terá uma parte muito agradável de documentação sentado na frente de você. Você também descobrirá que é muito mais fácil escrever este documento no início do projeto quando sua excitação e motivação estiverem em seu nível mais alto. Escrever retroativamente um Readme é um arrasto absoluto, e você está certo de perder todos os tipos de detalhes importantes quando você fizer isso.

    • Se você estiver trabalhando com uma equipe de desenvolvedores, você obterá ainda mais milhagem do seu Readme. Se todos os outros membros da equipe tiverem acesso a essas informações antes de concluírem o projeto, elas poderão começar a trabalhar com confiança em outros projetos que irão interagir com seu código. Sem qualquer tipo de interface definida, você tem que codificar em série ou face reimplementing grandes porções de código.

    • É muito mais simples ter uma discussão baseada em algo escrito. É fácil falar sem parar e em círculos sobre um problema se nada for colocado no texto. O simples ato de escrever uma solução proposta significa que todos têm uma idéia concreta que pode ser discutida e iterada.


Considere o processo de escrever o Readme para o seu projeto como o verdadeiro ato de criação. É aqui que todas as suas idéias brilhantes devem ser expressas. Este documento deve ficar por conta própria como um testemunho de sua criatividade e expressividade. O Readme deve ser o documento mais importante na sua base de código; Escrevê-lo primeiro é a coisa certa a fazer.


Versionamento Semântico 2.0.0

Design Patterns

Sumário

Dado um número de versão MAJOR.MINOR.PATCH, incremente a:

  • versão Maior(MAJOR): quando fizer mudanças incompatíveis na API,
  • versão Menor(MINOR): quando adicionar funcionalidades mantendo compatibilidade, e
  • versão de Correção(PATCH): quando corrigir falhas mantendo compatibilidade.

Rótulos adicionais para pré-lançamento(pre-release) e metadados de construção(build) estão disponíveis como extensão ao formato MAJOR.MINOR.PATCH.

Introdução

No mundo de gerenciamento de software existe algo terrível conhecido como inferno das dependências (“dependency hell”). Quanto mais o sistema cresce, e mais pacotes são adicionados a ele, maior será a possibilidade de, um dia, você encontrar-se neste poço de desespero.

Em sistemas com muitas dependências, lançar novos pacotes de versões pode se tornar rapidamente um pesadelo. Se as especificações das dependências são muito amarradas você corre o risco de um bloqueio de versão (A falta de capacidade de atualizar um pacote sem ter de liberar novas versões de cada pacote dependente). Se as dependências são vagamente especificadas, você irá inevitavelmente ser mordido pela ‘promiscuidade da versão’ (assumindo compatibilidade com futuras versões mais do que é razoável). O inferno das dependências é onde você está quando um bloqueio de versão e/ou promiscuidade de versão te impede de seguir em frente com seu projeto de maneira fácil e segura.

Como uma solução para este problema proponho um conjunto simples de regras e requisitos que ditam como os números das versões são atribuídos e incrementados.

Essas regras são baseadas em, mas não necessariamente limitadas às, bem difundidas práticas comumente em uso tanto em softwares fechados como open-source. Para que este sistema funcione, primeiro você precisa declarar uma API pública. Isto pode consistir de documentação ou ser determinada pelo próprio código. De qualquer maneira, é importante que esta API seja clara e precisa. Depois de identificada a API pública, você comunica as mudanças com incrementos específicos para o seu número de versão. Considere o formato de versão X.Y.Z (Maior.Menor.Correção). Correção de falhas (bug fixes) que não afetam a API, incrementa a versão de Correção, adições/alterações compatíveis com as versões anteriores da API incrementa a versão Menor, e alterações incompatíveis com as versões anteriores da API incrementa a versão Maior.

Eu chamo esse sistema de “Versionamento Semântico”. Sob este esquema, os números de versão e a forma como eles mudam transmitem o significado do código subjacente e o que foi modificado de uma versão para a próxima.

Especificação de Versionamento Semântico

http://semver.org/lang/pt-BR/


As palavras-chaves “DEVE”, “NÃO DEVE”, “OBRIGATÓRIO”, “DEVERÁ”, “NÃO DEVERÁ”, “PODEM”, “NÃO PODEM”, “RECOMENDADO”, “PODE” e “OPCIONAL” no presente documento devem ser interpretados como descrito na [RFC 2119] (http://tools.ietf.org/html/rfc2119).

  • Software usando Versionamento Semântico DEVE declarar uma API pública. Esta API poderá ser declarada no próprio código ou existir estritamente na documentação, desde que seja precisa e compreensiva.

  • Um número de versão normal DEVE ter o formato de X.Y.Z, onde X, Y, e Z são inteiros não negativos, e NÃO DEVE conter zeros à esquerda. X é a versão Maior, Y é a versão Menor, e Z é a versão de Correção. Cada elemento DEVE aumentar numericamente. Por exemplo: 1.9.0 -> 1.10.0 -> 1.11.0.

  • Uma vez que um pacote versionado foi lançado(released), o conteúdo desta versão NÃO DEVE ser modificado. Qualquer modificação DEVE ser lançado como uma nova versão.

  • No início do desenvolvimento, a versão Maior DEVE ser zero (0.y.z). Qualquer coisa pode mudar a qualquer momento. A API pública não deve ser considerada estável.

  • Versão 1.0.0 define a API como pública. A maneira como o número de versão é incrementado após este lançamento é dependente da API pública e como ela muda.

  • Versão de Correção Z (x.y.Z | x > 0) DEVE ser incrementado apenas se mantiver compatibilidade e introduzir correção de bugs. Uma correção de bug é definida como uma mudança interna que corrige um comportamento incorreto.

  • Versão Menor Y (x.Y.z | x > 0) DEVE ser incrementada se uma funcionalidade nova e compatível for introduzida na API pública. DEVE ser incrementada se qualquer funcionalidade da API pública for definida como descontinuada. PODE ser incrementada se uma nova funcionalidade ou melhoria substancial for introduzida dentro do código privado. PODE incluir mudanças a nível de correção. A versão de Correção deve ser redefinida para 0(zero) quando a versão Menor for incrementada.

  • Versão Maior X (X.y.z | X > 0) DEVE ser incrementada se forem introduzidas mudanças incompatíveis na API pública. PODE incluir alterações a nível de versão Menor e de versão de Correção. Versão de Correção e Versão Menor devem ser redefinidas para 0(zero) quando a versão Maior for incrementada.

  • Uma versão de Pré-Lançamento (pre-release) PODE ser identificada adicionando um hífen (dash) e uma série de identificadores separados por ponto (dot) imediatamente após a versão de Correção. Identificador DEVE incluir apenas caracteres alfanuméricos e hífen [0-9A-Za-z-]. Identificador NÃO DEVE ser vazio. Indicador numérico NÃO DEVE incluir zeros à esquerda. Versão de Pré-Lançamento tem precedência inferior à versão normal a que está associada. Uma versão de Pré-Lançamento (pre-release) indica que a versão é instável e pode não satisfazer os requisitos de compatibilidade pretendidos, como indicado por sua versão normal associada. Exemplos: 1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-0.3.7, 1.0.0-x.7.z.92.

  • Metadados de construção(Build) PODE ser identificada por adicionar um sinal de adição (+) e uma série de identificadores separados por ponto imediatamente após a Correção ou Pré-Lançamento. Identificador DEVE ser composto apenas por caracteres alfanuméricos e hífen [0-9A-Za-z-]. Identificador NÃO DEVE ser vazio. Metadados de construção PODEM ser ignorados quando se determina a versão de precedência. Assim, duas versões que diferem apenas nos metadados de construção, têm a mesma precedência. Exemplos: 1.0.0-alpha+001, 1.0.0+20130313144700, 1.0.0-beta+exp.sha.5114f85.

  • A precedência refere como as versões são comparadas com cada outra quando solicitado. A precedência DEVE ser calculada separando identificadores de versão em Maior, Menor, Correção e Pré-lançamento, nesta ordem (Metadados de construção não figuram na precedência). A precedência é determinada pela primeira diferença quando se compara cada identificador da esquerda para direita, como se segue: Versões Maior, Menor e Correção são sempre comparadas numericamente. Example: 1.0.0 < 2.0.0 < 2.1.0 < 2.1.1. Quando Maior, Menor e Correção são iguais, a versão de Pré-Lançamento tem precedência menor que a versão normal. Example: 1.0.0-alpha < 1.0.0. A precedência entre duas versões de Pré-lançamento com mesma versão Maior, Menor e Correção DEVE ser determinada comparando cada identificador separado por ponto da esquerda para direita até que seja encontrada diferença da seguinte forma: identificadores consistindo apenas dígitos são comparados numericamente e identificadores com letras ou hífen são comparados lexicalmente na ordem de classificação ASCII. Identificadores numéricos sempre têm menor precedência do que os não numéricos. Um conjunto maior de campos de pré-lançamento tem uma precedência maior do que um conjunto menor, se todos os identificadores anteriores são iguais. Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0.

Por que usar Versionamento Semântico?

Esta não é uma ideia nova ou revolucionária. De fato, você provavelmente já faz algo próximo a isso. O problema é que “próximo” não é bom o bastante. Sem a aderência a algum tipo de especificação formal, os números de versão são essencialmente inúteis para gerenciamento de dependências. Dando um nome e definições claras às ideias acima, fica fácil comunicar suas intenções aos usuários de seu software. Uma vez que estas intenções estão claras, especificações de dependências flexíveis (mas não tão flexíveis) finalmente podem ser feitas.

Um exemplo simples vai demonstrar como o Versionamento Semântico pode fazer do inferno de dependência uma coisa do passado. Considere uma biblioteca chamada “CaminhaoBombeiros”. Ela requer um pacote versionado dinamicamente chamado “Escada”. Quando CaminhaoBombeiros foi criado, Escada estava na versão 3.1.0. Como CaminhaoBombeiros utiliza algumas funcionalidades que foram inicialmente introduzidas na versão 3.1.0, você pode especificar, com segurança, a dependência da Escada como maior ou igual a 3.1.0 porém menor que 4.0.0. Agora, quando Escada versão 3.1.1 e 3.2.0 estiverem disponíveis, você poderá lança-los ao seu sistema de gerenciamento de pacote e saberá que eles serão compatíveis com os softwares dependentes existentes.

Como um desenvolvedor responsável você irá, é claro, querer certificar-se que qualquer atualização no pacote funcionará como anunciado. O mundo real é um lugar bagunçado; não há nada que possamos fazer quanto a isso senão sermos vigilantes. O que você pode fazer é deixar o Versionamento Semântico lhe fornecer uma maneira sensata de lançar e atualizar pacotes sem precisar atualizar para novas versões de pacotes dependentes, salvando-lhe tempo e aborrecimento.

Se tudo isto soa desejável, tudo que você precisar fazer para começar a usar Versionamento Semântico é declarar que você o esta usando e então, seguir as regras. Adicione um link para este website no seu README para que outros saibam as regras e possam beneficiar-se delas.

FAQ

Como devo lidar com revisões na fase 0.y.z de desenvolvimento inicial?

A coisa mais simples a se fazer é começar sua versão de desenvolvimento inicial em 0.1.0 e, então, incrementar a uma versão ‘menor’ em cada lançamento subsequente.

Como eu sei quando lançar a versão 1.0.0?

Se seu software está sendo usado em produção, ele já deve ser provavelmente 1.0.0. Se você possui uma API estável a qual usuários passaram a depender, deve ser 1.0.0. Se você está se preocupando bastante com compatibilidade com versões anteriores, já deve ser 1.0.0.

Isto não desencoraja o desenvolvimento ágil e iteração rápida?

A versão Maior zero tem o foco exatamente no desenvolvimento rápido. Se você está mudando a API todo dia, provavelmente você está na versão 0.y.z ou num branch separado de desenvolvimento, trabalhando numa próxima versão Maior.

Se mesmo a menor mudança incompatível com a API pública requer aumento da versão maior, não vou acabar na versão 42.0.0 muito rapidamente?

Esta é uma questão de desenvolvimento responsável e conhecimento antecipado. Mudanças incompatíveis não devem ser levemente introduzidas para o software que tem um monte de código dependente. O custo que deve ser incorrido para atualizar pode ser significante. Tendo que aumentar a versão maior para lançar mudanças incompatíveis, significa que você pensará no impacto das suas mudanças, e avaliará a relação de custo/benefício envolvida.

Documentar toda a API pública dá muito trabalho!

É sua responsabilidade como desenvolvedor profissional documentar corretamente o software que será usado por outros. Gerenciar a complexidade de software é uma parte muito importante para manter o projeto eficiente, e isto é difícil de fazer se ninguém sabe como usá-lo ou que métodos são seguros de chamar. A longo prazo, Versionamento Semântico e a insistência em uma API pública bem definida podem deixar tudo e todos funcionando suavemente.

O que eu faço se, acidentalmente, liberar uma mudança incompatível com versões anteriores como uma versão menor (minor version)?

Assim que você perceber que quebrou a especificação de versionamento semântico, conserte o problema e lance uma nova versão menor, que corrige o problema e restaura a compatibilidade. Mesmo sob esta circunstância, é inaceitável modificar versões lançadas. Se for apropriado, documente a versão ofensiva e informe seus usuários do problema de forma que eles fiquem cientes da versão em questão.

O que devo fazer se eu atualizar minhas próprias dependências sem modificar a API pública?

Isso seria considerado compatível, uma vez que não afeta a API pública. Software que depende explicitamente da mesmas dependências que seu pacote, deve ter sua própria especificação de dependência e o autor notificará quaisquer conflitos. Para determinar se a mudança é a nível de correção ou modificação de nível menor dependente se você atualizou suas dependências a fim de corrigir um bug ou introduzir nova funcionalidade. Eu normalmente esperaria código adicional para última instância, caso em que é obviamente um incremento no nível menor.

E se eu alterei inadvertidamente a API pública de forma incompatível com a mudança no número de versão (ex.: o código incorretamente introduz uma grande mudança incompatível em liberação de um patch)

Use o bom senso. Se você tem um público enorme que será drasticamente impactado pela mudança de comportamento de volta para o que a API pública pretendida, então pode ser melhor realizar um lançamento de uma versão maior, mesmo que a correção pudesse ser considerada estritamente uma versão de correção. Lembre-se, Versionamento Semântico trata de transmitir o conhecimento das mudanças ocorridas na versão. Se estas mudanças são importantes para seus usuários, utilize o número da versão para informá-los.

Como devo lidar com descontinuação de funcionalidades?

Descontinuar funcionalidades é um processo comum no desenvolvimento de software e muitas vezes é necessário para haver progresso. Quando você descontinua partes de sua API pública, você deve fazer duas coisas: (1) atualizar sua documentação, para que os usuários saibam das mudanças, (2) lançar uma versão Menor anunciando a descontinuação. Antes de remover completamente a funcionalidade em uma versão Maior deve haver ao menos uma versão Menor que possui a descontinução anunciada, fazendo com que os usuários realizem uma transição tranquila para a nova API.

O SemVer tem um limite de tamanho para string de versão?

Não, mas use o bom senso. Uma string de versão com 255 caracteres por exemplo, provavelmente é um exagero. Porém, sistemas específicos podem definir seus prórios limites para o tamanho da string.