Existe uma correlação entre a escala do projeto e o rigor da linguagem?

72

Explicando a diferença entre rigor de linguagens e paradigmas para um colega meu, acabei afirmando que:

  • Linguagens tolerantes, como linguagens dinâmicas e interpretadas, são usadas melhor para protótipos e projetos pequenos ou aplicativos Web de tamanho médio. Ao escolher linguagens dinâmicas elegantes, como Python ou JavaScript com Node.js, os benefícios são:

    1. Desenvolvimento rápido,

    2. Código de clichê reduzido

    3. Capacidade de atrair programadores jovens e criativos que fogem de "linguagens corporativas"   como Java.

  • Os idiomas estaticamente digitados / compilados são melhores para aplicativos que exigem maior rigor, como aplicativos críticos para os negócios ou aplicativos para aplicativos de tamanho médio a grande.

    1. Paradigmas e padrões conhecidos desenvolvidos por décadas,

    2. Facilidade de verificação estática,

    3. Capacidade de encontrar muitos desenvolvedores profissionais com décadas de experiência.

  • Linguagens estritas como Haskell, Ada ou técnicas como contratos de código em C # são melhores para sistemas que favorecem a segurança do que a flexibilidade (mesmo que a Haskell possa ser extremamente flexível), como sistemas críticos à vida e sistemas que se espera sejam extremamente estáveis. Os benefícios são:

    1. Capacidade de capturar o maior número possível de bugs em tempo de compilação,

    2. Facilidade de verificação estática,

    3. Facilidade de provas formais.

No entanto, olhando as linguagens e tecnologias usadas em projetos de larga escala por grandes corporações, parece que minha afirmação está errada . Por exemplo, o Python é usado com êxito em sistemas grandes, como o YouTube ou outros aplicativos do Google, que exigem uma quantidade importante de rigor.

Ainda existe uma correlação entre a escala do projeto e o rigor da linguagem / paradigma que deve ser usado?

Existe um terceiro fator que eu esqueci de levar em conta?

Onde eu estou errado?

Arseni Mourzenko
fonte
12
Verificação estrita de tipo e verificação estática de tipo não são a mesma coisa. O Python é digitado dinamicamente, mas é mais rigoroso que C. A vantagem da verificação estática de tipos não é a rigidez per se, mas esses tipos são verificados no tempo de construção, não no tempo de execução. Eu lidei com muitos problemas de C / C ++ em minha carreira por causa de elenco implícito.
Gort the Robot
5
Provavelmente há algo a ser dito sobre o ciclo de vida: o software que começa na sua primeira categoria pode evoluir para as outras, "arrastando" o idioma com ele.
24513 Mat
11
A única coisa elegante sobre o javascript é que ele é executado na maioria dos navegadores.
Jeffo
11
@StevenBurnap: Eu não poderia concordar mais sobre a diferença entre estático e estrito. Java é outro ponto do espectro, sendo estático e muito rigoroso. Os desenvolvedores costumam censurar a digitação estática usando Java como exemplo, mas muitas dessas críticas devem ser direcionadas ao compilador excessivamente rígido do Java , e não à digitação estática em geral. Basta olhar para o Scala na mesma JVM, que é estaticamente tipada, mas possui um código muito menos detalhado devido às habilidades de inferir tipos de tipos do compilador fantástico.
Cornel Masson
2
"Python é usado com sucesso para sistemas grandes" - qual é a definição de "sucesso" aqui? Que ele roda principalmente e produz algum resultado? A quantidade de testes e força de trabalho necessária está incluída? E quanto à manutenção?
Den

Respostas:

39

Um estudo de caso interessante sobre os assuntos de projetos de dimensionamento que usam linguagem dinâmica e interpretada pode ser encontrado em Beginning Scala por David Pollak.

Comecei a procurar uma maneira de expressar o código no meu cérebro de uma maneira mais simples e direta. Encontrei Ruby e Rails. Eu me senti liberto. Ruby me permitiu expressar conceitos em muito menos linhas de código. O Rails era muito mais fácil de usar do que o Spring MVC, o Hibernate e os outros frameworks da Web Java “simplificados”. Com Ruby e Rails, eu consegui expressar muito mais do que estava na minha cabeça em um curto período de tempo. Foi semelhante à libertação que senti quando mudei de C ++ para Java ...

À medida que meus projetos Ruby e Rails cresceram além de alguns milhares de linhas de código e à medida que adicionei membros da equipe aos meus projetos, os desafios das linguagens dinâmicas se tornaram aparentes.

Estávamos gastando mais da metade do tempo de codificação escrevendo testes, e grande parte dos ganhos de produtividade que vimos foram perdidos na escrita de testes . A maioria dos testes teria sido desnecessária em Java porque a maioria deles era voltada para garantir que atualizássemos os chamadores quando refatoramos o código alterando nomes de métodos ou contagens de parâmetros. Além disso, descobri que, trabalhando em equipes nas quais havia confusão mental entre dois e quatro membros da equipe, as coisas correram bem em Ruby, mas quando tentamos trazer novos membros para a equipe, as conexões mentais eram difíceis de transmitir aos novos membros da equipe .

Procurei um novo ambiente de linguagem e desenvolvimento. Eu estava procurando por uma linguagem tão expressiva quanto Ruby, mas tão segura e de alto desempenho quanto Java ...

Como você pode ver, os principais desafios na escala do projeto para o autor acabaram sendo no desenvolvimento de testes e na transferência de conhecimento.

Em particular, o autor entra em mais detalhes ao explicar as diferenças na escrita de teste entre idiomas de tipo dinâmico e estaticamente no Capítulo 7. Na seção "Matando com tristeza coelhinhos: escadas de Dwemthy", o autor discute a porta Scala de um exemplo específico de Ruby:

Por que o Lucky Stiff ... introduz alguns dos conceitos de metaprogramação de Ruby em Dwemthy's Array, nos quais um coelho luta contra uma variedade de criaturas. N8han14 atualizou o exemplo para trabalhar em Scala ...

Comparado com o código Ruby, as partes da biblioteca do código Scala eram mais complexas. Tivemos que trabalhar muito para garantir que nossos tipos estavam corretos. Tivemos que reescrever manualmente as propriedades do Creature nas classes DupMonster e CreatureCons. Isso é mais trabalho do que method_missing. Também tivemos que trabalhar bastante para apoiar a imutabilidade em nossas criaturas e armas.

Por outro lado, o resultado foi muito mais poderoso que a versão Ruby. Se tivéssemos que escrever testes para o nosso código Ruby para testar o que o compilador Scala nos garante, precisaríamos de muito mais linhas de código. Por exemplo, podemos ter certeza de que nosso coelho não poderia empunhar um machado. Para obter essa garantia em Ruby, teríamos que escrever um teste que garanta que a chamada |^em um Rabbit falhe. Nossa versão Scala garante que apenas as Armas definidas para uma determinada Criatura possam ser usadas por essa Criatura, algo que exigiria muita reflexão em tempo de execução em Ruby ...


A leitura acima pode fazer alguém pensar que, à medida que os projetos aumentam ainda mais, a escrita de teste pode se tornar proibitivamente complicada. Esse raciocínio estaria errado, como evidenciado por exemplos de projetos muito grandes e bem-sucedidos mencionados nesta mesma pergunta ("Python é usado com sucesso para ... YouTube").

O fato é que a escala dos projetos não é realmente direta. Projetos muito grandes e de longa duração podem "suportar" diferentes processos de desenvolvimento de testes, com suítes de testes de qualidade de produção, equipes de desenvolvimento de testes profissionais e outras coisas pesadas.

Os conjuntos de testes do YouTube ou o Java Compatibility Kit têm uma vida diferente dos testes em um pequeno projeto tutorial como o Dwemthy's Array .

mosquito
fonte
24

Sua afirmação não está errada. Você só precisa cavar um pouco mais fundo.

Simplificando, os grandes sistemas usam vários idiomas, não apenas um idioma. Pode haver partes criadas usando linguagens "estritas" e partes criadas usando linguagens dinâmicas.

Quanto ao seu exemplo do Google e do YouTube, ouvi dizer que eles usam o Python principalmente como "cola" entre vários sistemas. Somente o Google sabe com o que esses sistemas são construídos, mas aposto que muitos dos sistemas críticos do Google são construídos usando linguagens estritas e "corporativas", como C ++ ou Java, ou talvez algo que eles mesmos tenham criado como Go.

Não é que você não possa usar linguagens tolerantes para sistemas em grande escala. Muitas pessoas dizem que o Facebook usa PHP, mas esquecem de mencionar que o Facebook precisou criar diretrizes de programação extremamente rígidas para usá-lo com eficiência nessa escala.

Então, sim, é necessário algum nível de rigor para projetos de grande escala. Isso pode advir do rigor da linguagem ou estrutura, ou das diretrizes de programação e convenções de código. Você não pode simplesmente pegar alguns graduados, dar-lhes Python / Ruby / JavaScript e esperar que eles escrevam um software que escala milhões de usuários.

Eufórico
fonte
"Você não pode simplesmente pegar alguns graduados da faculdade" ... "e esperar que eles escrevam um software que atinja milhões de usuários." provavelmente teria sido suficiente.
dyesdyes
Vale a pena notar aqui que, como no Google e Python, o uso do PHP pelo Facebook é amplamente como cola ... Meu entendimento é que, para a maioria das funções, o PHP é usado apenas como um cliente relativamente simples em um sistema de servidor mais complexo que normalmente é implementado em uma linguagem "tradicional" mais tradicional, como Java, C ++, Haskell, OCaML etc.
Jules
"Somente o Google sabe com o que esses sistemas são construídos" .. Tenho até algumas dúvidas sobre isso :) Na minha experiência, nenhuma entidade (pessoa ou não) pode listar todas as partes de um sistema muito grande. Em muitos casos, enterrado nas tigelas de algum servidor, há um pedaço esquecido do script Perl, Fortran ou KSH que executa 'Magic'.
18717 mattnz
3

Há dois tipos de erros a serem verificados: erros de tipo (concatenar um número inteiro + lista de flutuações) e erros de lógica de negócios (transferir dinheiro para uma conta bancária, verificar se a conta de origem tem dinheiro).

A parte "dinâmica" de uma linguagem de programação dinâmica é apenas o local onde a verificação de tipo ocorre. Em uma linguagem de programação "digitada dinamicamente", a verificação de tipo é feita durante a execução de cada instrução, enquanto em uma linguagem de linguagem "digitada estaticamente" é feita no momento da compilação. E você pode escrever um intérprete para uma linguagem de programação estática (como o emscriptem ) e também pode escrever um compilador estático para uma linguagem de programação dinâmica (como gcc-python ou shed-skin ).

Em uma linguagem de programação dinâmica como Python e Javascript, você precisa escrever testes de unidade não apenas para a lógica de negócios do programa, mas também para verificar se o seu programa não possui erros de sintaxe ou de tipo. Por exemplo, se você adicionar "+" um número inteiro a uma lista de carros alegóricos (o que não faz sentido e emitirá um erro), em um idioma dinâmico, o erro será gerado no tempo de execução ao tentar executar a instrução. Em uma linguagem de programação estática como C ++, Haskell e Java, esse tipo de erro de tipo será detectado pelo compilador.

Uma pequena base de código em uma linguagem de programação verificada dinamicamente é mais fácil de procurar erros de tipo, porque é mais fácil ter uma cobertura de 100% do código-fonte. É isso, você executa o código manualmente algumas vezes com valores diferentes e pronto. Ter uma cobertura de 100% do código fonte fornece uma boa dica de que seu programa pode não ter erros de tipo .

Com uma grande base de código em uma linguagem de programação verificada dinamicamente, é mais difícil testar todas as instruções com todas as combinações de tipos possíveis, especialmente se você for descuidado e escrever uma função que possa retornar uma string, lista ou objeto personalizado, dependendo de seus argumentos.

Em uma linguagem de programação verificada estaticamente, o compilador captura a maioria dos erros de tipo em tempo de compilação. Eu digo mais porque uma divisão por erro zero ou um erro de matriz fora dos limites também são erros de tipo.

Frequentemente, a discussão real não é sobre linguagens de programação, mas sobre as pessoas que as utilizam. E isso é verdade porque, por exemplo, a linguagem assembly é tão poderosa quanto qualquer outra linguagem de programação, mas estamos escrevendo código em JavaScript. Por quê? Porque nós somos humanos. Primeiro, todos cometemos erros e é mais fácil e menos propenso a usar uma ferramenta dedicada especializada para uma tarefa específica. Segundo, há uma restrição de recursos. Nosso tempo é limitado, e escrever páginas da Web em montagem levaria séculos para terminar.

vz0
fonte
3

Minha experiência com sistemas grandes é que eles se mantêm ou não por escolha de idioma, mas por questões de design / arquitetura ou cobertura de teste . Prefiro ter uma equipe Python talentosa no meu grande projeto empresarial, do que um Java medíocre.

Dito isto, qualquer linguagem que permita escrever significativamente menos código deve valer a pena ser observada (por exemplo, Python x Java). Talvez o futuro esteja em linguagens inteligentes, de tipo estatístico, com inferência de tipo avançada (por exemplo, no molde Scala). Ou híbrido, como C # está tentando com seu dynamicqualificador ...?

E não vamos esquecer o "outro" benefício da digitação estática: a conclusão adequada do código IDE / intellisense, que, a meu ver, é um recurso essencial, não um bom de se ter.

Cornel Masson
fonte
11
"conclusão de código / intellisense" - refatoração automatizada também é bastante importante.
Den
@ Den Absolutamente. Será que as linguagens dinâmicas o ajudam a escrever versões iniciais muito rapidamente (mais fáceis, com menos código para escrever), mas ficam atoladas mais tarde, pois fica cada vez mais difícil avaliar o impacto das mudanças ou fazer a refatoração (sem ferramentas de refatoração automatizadas)?
Cornel Masson
0

Outra consideração é quem está por trás da criação de aplicativos em larga escala. Já trabalhei em vários lugares que desejam usar Ruby ou Python em alguns grandes projetos de estilo corporativo, mas são consistentemente "derrubados" por gerentes de TI e equipes de segurança corporativa precisamente devido à natureza de código aberto dos projetos.

Foi-me dito: "Não podemos usar o Ruby on Rails porque é de código aberto e alguém pode colocar hacks que roubam informações críticas ou protegidas". Sinto muito, mas uma vez que alguém tenha essa mentalidade de código aberto == mal, é quase impossível mudar isso. Essa linha de pensamento é uma doença corporativa.

C # e Java são confiáveis línguas com confiáveis plataformas. Ruby e Python não são idiomas confiáveis.

Jarrett Meyer
fonte
2
Eu discordo da última linha. Java está em um de seus pontos de confiança mais baixos de todos os tempos. O C # é considerado com cautela por toda a comunidade de código aberto. Ruby é visto como sólido, mas lento (embora não seja mais) e Python é o filho do glamour de cavalos de trabalho confiáveis ​​de todos os setores (aprendizado de máquina e ciência de dados, alguém?).
CodeBeard
11
Linguagens dinâmicas são ruins para a segurança, mas "código aberto" não é uma boa razão. Talvez eles quisessem dizer "é fácil influenciar uma parte do código de uma parte completamente diferente do código". Veja programmers.stackexchange.com/questions/206558/…
Euphoric
11
Observe que, de fato, "código aberto" é um dos aspectos de escolha de um idioma. Essa é, por exemplo, uma das três razões apresentadas por Jeff Atwood para explicar por que o Discourse usa Ruby.
Arseni Mourzenko
O C # é completamente de código aberto agora, mas ainda é organizado, planejado e desenvolvido por desenvolvedores profissionais, o que é legal, eu acho. Vamos torcer para que o tipo "Python 3 vs 2" não aconteça aqui.
Den
Bugs e falhas de segurança são introduzidos por programadores e não por idiomas, e para o registro, eu contribuí com várias correções de segurança em projetos de código aberto. Quantos projetos fechados eu ajudei ??? zero!
Reactgular