Qual linguagem de programação gera menos bugs difíceis de encontrar? [fechadas]


54

Qual idioma, na sua opinião, permite que o programador médio produza recursos com a menor quantidade de bugs difíceis de encontrar? Naturalmente, essa é uma pergunta muito ampla, e estou interessado em respostas e conhecimentos muito amplos e gerais.

Pessoalmente, acho que passo muito pouco tempo procurando bugs estranhos nos programas Java e C #, enquanto o código C ++ tem seu conjunto distinto de bugs recorrentes, e o Python / similar tem seu próprio conjunto de bugs comuns e tolos que seriam detectados pelo compilador em outros idiomas.

Também acho difícil considerar linguagens funcionais nesse sentido, porque nunca vi um programa grande e complexo escrito em código totalmente funcional. Sua entrada, por favor.

Editar: Clarificação completamente arbitrária do bug difícil de encontrar: leva mais de 15 minutos para reproduzir ou mais de 1 hora para encontrar a causa e a correção.

Perdoe-me se for uma duplicata, mas não encontrei nada sobre esse tópico específico.


10
Eu gostaria de ver algumas pesquisas feitas sobre esse tópico! Não apenas "minhas evidências anedóticas sugerem que o único idioma que conheço é rei", mas as taxas de bugs de grandes projetos e assim por diante.
Frank Shearar

@Frank .. se você tivesse MUITO (e quero dizer MUITO) tempo, provavelmente poderia extrair algumas estatísticas de ohloh, desde que pudesse identificar patches que corrigiam bugs de milhares de repositórios de código.
Tim Post

Aquele em que apenas comentários são permitidos. Nenhuma outra instrução :)
Victor Hurdugaci

4
Eu acho que "difícil de encontrar" precisa ser esclarecido. Sua pergunta e a maioria das respostas parecem definir "difícil de encontrar" como sendo equivalente a "não capturado pelo compilador". Você menciona o python como tendo erros bobos que seriam detectados pelo compilador. Justo. Mas esses erros bobos geralmente não são tão difíceis de encontrar. Certamente, eles não estão na mesma categoria que os erros de C ++ decorrentes de, por exemplo, liberação de memória muito cedo.
Winston Ewert

@Winston Concordo.
Magnus Wolffelt

Respostas:


58

Quanto mais poderoso o sistema de tipos de linguagem, mais erros serão detectados no momento da compilação.

A figura a seguir compara algumas das linguagens de programação conhecidas em termos de potência, simplicidade e segurança de seus sistemas de tipos. [ Fonte ]

texto alternativo

* Factoring na capacidade de usar construções inseguras.

O C # é inserido na linha insegura por causa da palavra-chave "insegura" e do mecanismo de ponteiro associado. Mas se você quiser pensar neles como um tipo de mecanismo interno de função externa, sinta-se à vontade para esbarrar em C # em direção ao céu.

Marquei Haskell '98 como puro, mas GHC Haskell como não puro devido à família insegura de funções *. Se você desativar o * não seguro, pule o GHC Haskell para cima.


8
Isto é brilhante!

7
Não consegui encontrar o Common Lisp no gráfico: (let ((itbe '())) ... )...
duros

7
O que? Não havia mais espaço no lado esquerdo para PHP?
Pestaa

7
O C # permite indicadores seguros : toda a aritmética do indicador está marcada. Portanto, acredito que deveria estar lá em cima com Java.
Alex ten Brink

11
@missingfaktor: Eu acho que o C # não está bem posicionado. O C # permite e promove estilos de programação mais seguros. Não deve ser medido pela pior coisa que permite.
dez2

20

Na minha opinião, Haskell ajuda a evitar algumas fontes comuns de erros:

  • é puramente funcional: as funções não podem ter efeitos colaterais (não intencionais) e isso torna a programação multicore mais fácil e menos propensa a erros
  • é fortemente tipado: você não pode, por exemplo, misturar acidentalmente valores bool, char, int e float
  • é digitado estaticamente: muitos erros de programação são detectados em tempo de compilação
  • nullnão faz parte das definições de tipo de valor: com isso, você evita o erro de um bilhão de dólares
  • existem muitas funções de ordem superior prontas que você pode reutilizar em vez de escrever suas próprias implementações, possivelmente defeituosas
  • possui um coletor de lixo: os erros de memória são quase eliminados (exceto "vazamentos de espaço" devido à sua estratégia de avaliação lenta)

11
Não vou diminuir isso, mas estou tentado, porque não se encaixa nos critérios. Ele deveria permitir que "o programador médio produza recursos" com uma contagem mínima de bugs, mas o programador médio, para ser franco, não pode, em primeiro lugar, fazer cara ou coroa com Haskell.
Mason Wheeler

5
Muitos pontos positivos, mas ao remover algumas classes de erro (efeitos colaterais indesejados, conversões inesperadas de tipo) Haskell adiciona uma nova classe de erro: vazamentos de espaço. (Além disso, embora não existe null, existe undefined, o que é um membro de cada tipo.)
j_random_hacker

5
@Mason: Se você não escrever nele, você não pode ter erros int ele :)
Michael K

2
Eu acho que não há tal coisa como um almoço livre - você pode ter erros fácil de encontrar, ou fácil de escrever o código, mas não ambos :)
Benjol

6
@Mason o que exatamente é um “programador médio”? A pergunta começa com "qual linguagem de programação ...", não "qual entre C, C ++ e java ...";)
Agos

18

Tradicionalmente, os bugs mais difíceis de encontrar são as condições de corrida em aplicativos multithread como estão.

  • quase impossível de reproduzir
  • pode ser muito sutil

Portanto, você precisa de linguagens que gerenciem o paralelismo para você o máximo e sem intrusões possível. Estes ainda não são comuns. Java faz alguns, mas deixa você com a parte mais difícil.

No meu entender, você precisa de uma linguagem funcional, já que o "sem efeitos colaterais" é o que, em primeiro lugar, faz com que os dois pontos de bala desapareçam. Vi que o trabalho está em andamento para tornar o Haskell de maneira transparente uma linguagem multiencadeada eficiente, e acredito que o Fortress foi projetado desde o início para ser uma linguagem paralela eficiente.


Editar: No Java, Executorslida com ainda mais partes difíceis. Você precisa tornar as tarefas individuais em conformidade com a Callableinterface.


5
++ Condições da corrida. Você pode dizer isso de novo.
Mike Dunlavey

Sim. Lembre-se de que a computação paralela em geral é difícil, mesmo com a assistência ao idioma. (Macros Lisp Comum são difíceis, apesar de um monte de suporte ao idioma, porque o que eles fazem é muito poderoso mesmo princípio..)
David Thornley

E Erlang?
Malfist

@ Malfist, eu não sei o suficiente sobre Erlang para responder a isso. Talvez você deva abrir uma pergunta se realmente quer saber?

2
Erlang é uma programação funcional projetada para tornar o multithreading simples e seguro. Você não compartilha variáveis, passa mensagens. Leia a página da Wikipedia.
Malfist

13

O Ada foi projetado para que o máximo possível seja capturado no tempo de compilação, em vez do tempo de execução. O que isso significa é que geralmente leva cerca de 10x mais tempo para que um programa no Ada seja compilado do que o equivalente em Java diria, mas quando ele é compilado, você pode ficar muito mais confiante de que classes inteiras de bugs não se manifestam quando o programa é executado. corre.


7
+1 por perceber, corretamente, que Ada captura coisas no compilador que outros idiomas ignoram. -1 para a afirmação de que isso significa que leva 10 vezes mais tempo para que um programa Ada seja compilado. Ada premia o programador que DESIGN !!! o código dele, que PENSA !!! sobre o que ele está fazendo antes de começar a digitar loucamente. Minha experiência pessoal, fazendo programação de produção no Ada para o trabalho de defesa, foi que não demorou muito mais para compilar o código Ada do que o C / C ++ ou FORTRAN, mas o código Ada teve muito menos problemas posteriormente. Pratt & Whitney notaram algo semelhante.
John R. Strohm

11
@ john r strohm, pelo que entendi, ele não está falando sobre o tempo que leva para o compilador compilar o código, e sim o tempo para torná-lo compilável.
Malfist

oh concordou em obter o código compilável. Lembro-me de muitos comentários machistas feitos por programadores aprendendo a língua sobre sua escolha. Geralmente ao longo das linhas de Bem, se você nomear uma linguagem depois que uma mulher ...
Michael Shaw

@Ptolomeu, se os barcos podem ter o nome de mulheres (exceto aparentemente para as transportadoras americanas), as linguagens de programação também podem. Em vez tê-lo chamado de "Ada" do que "USS Ronald Reagan" :)

11
Quando superei a curva de aprendizado, na verdade, fiquei muito rápido ao escrever o código Ada - depois de passar bastante tempo pensando no design. Ada definitivamente não é a linguagem de um hacker. Atualmente trabalho em Java, e algumas das coisas que estou vendo nos meus projetos atuais realmente me fazem sentir falta de Ada um pouco (nunca pensei que diria isso).
James Adam

7

Primeiro, uma definição: um bug difícil de encontrar, pelo que entendi, é um bug que pode ser reproduzido, mas a causa é difícil de encontrar.

Provavelmente, o aspecto mais importante é o que eu chamaria de estreiteza , ou seja, até que ponto um bug pode escapar, qual o tamanho do escopo que um bug pode potencialmente influenciar. Em linguagens como C, um bug, por exemplo, um índice de matriz negativo ou ponteiro não inicializado, pode afetar literalmente tudo em qualquer lugar do programa, portanto, na pior das hipóteses, você deve verificar tudo em todos os lugares para encontrar a fonte do seu problema.

Bons idiomas nesse sentido oferecem suporte a modificadores de acesso e os impõem de uma maneira que dificulta ou impossibilita ignorá-los. Boas linguagens incentivam você a limitar o escopo de suas variáveis, em vez de facilitar a criação de variáveis ​​globais (por exemplo, "tudo o que não foi declarado explicitamente é uma variável global com um tipo e valor padrão").

O segundo aspecto importante é a simultaneidade . As condições da corrida são geralmente difíceis de reproduzir e, portanto, difíceis de encontrar. Boas linguagens oferecem mecanismos de sincronização fáceis de usar, e suas bibliotecas padrão são seguras contra threads sempre que necessário.

Isso já completa minha lista; outras coisas, como digitação forte, ajudam a detectar bugs em tempo de compilação, mas provavelmente não será difícil encontrá-los mais tarde.

Considerando tudo isso, eu diria que Java e C #, e muitas outras linguagens no mundo da JVM e .net, são adequados para evitar erros difíceis de encontrar.


O bloqueio manual pode parecer um "mecanismo de sincronização fácil de usar", mas na verdade é apenas "simples de usar, mas você passa momentos divertidos enquanto persegue esse impasse". E, bem, você pode fazer simultaneidade baseada em ator em vários idiomas.
precisa saber é o seguinte

Frank: pelo menos bloqueio é simples o suficiente para que todos possam fazê-lo sem gastar dias, descobrir qual API para usar etc.
user281377

Depois, há o ponto de vista de que uma solução simultânea que é "simples o suficiente para que todos possam fazê-lo" é como dar serras de mesa para pré-escolares.
David Thornley

@ David: Entendo o seu ponto, mas em muitos casos, uma solução simples é realmente tudo o que é preciso. Por exemplo, considere um servlet usado por apenas alguns usuários, que precisa usar um recurso compartilhado (por exemplo, a conexão com o banco de dados). Sem sincronização, as condições da corrida acontecem de vez em quando; mas não é exatamente a ciência do foguete colocar todas as operações de acesso ao banco de dados em um bloco sincronizado (conexão) {}.
User281377

+1 Para esta definição "qual o tamanho do escopo que um bug pode potencialmente influenciar". Eu tive alguns bugs muito desagradáveis ​​com linguagens dinâmicas, nos quais um objeto do tipo de dados errado ficou muito longe no código (graças à digitação do duck) antes de se manifestar como um bug.
Giorgio

7

Como o Excel é o DSL mais usado, irei ao Excel. (exceto VBA, é claro)

Ele se encaixa na conta:

  • É sempre fácil de reproduzir (aqui está uma planilha - não está funcionando)
  • É muito fácil encontrar o bug, pois é totalmente "funcional" - comece com a célula errada e rastreie todas as suas dependências.

Isso pode ser verdade desde que você não adicione bugs fáceis de encontrar.
Mouviciel 22/01

+1 Um pouco atrevido, pois o Excel é um dado, não um idioma. Dei-me uma boa risada :)
recursion.ninja:

2
@awashburn - oh, eu não sei. Eu acho que se qualifica como um idioma. Cada célula é uma "variável". Cada variável é declarativamente definida como literal (como 123ou ABC) ou como função ( =SUM(A2:A5)). O Excel avalia todas as variáveis, descobrindo em que ordem resolver as dependências, etc. Certamente não são apenas dados.
11118 Scott Whitlock

2
Eu retirar minha declaração, verifica-se que Excel é Turing completa ... eu aprendi algo totalmente perturbador ...
recursion.ninja

11
"... o verdadeiro mestre [Lisp] percebe que todos os dados são código." Talvez isso se aplique ao Excel também, em número suficiente. blogs.msdn.com/b/sriram/archive/2006/01/15/lisp-is-sin.aspx
James Mishra

7

Essa é uma pergunta difícil, porque a maioria dos erros não é culpa da própria linguagem - eles são cometidos por desenvolvedores cometendo erros na maneira como usam a linguagem.

Acredito que existem vários aspectos dos recursos de linguagem que afetam a probabilidade de erros:

  • Interatividade - linguagens dinâmicas com REPLs incentivam a interação / experimentação com programas em execução e ciclos de código / teste muito menores. Se você acredita que a iteração é uma boa maneira de descobrir soluções simples e limpas e detectar / eliminar bugs, isso tenderia a favorecer linguagens interativas.

  • Expressividade - se o código for mais curto e tiver menos complexidade padrão / incidental, será mais fácil ver bugs / erros lógicos.

  • Segurança do tipo - quanto mais a verificação do tempo de compilação, mais erros serão detectados pelo compilador, portanto, em geral, a segurança do tipo é uma coisa boa. No entanto, esses erros normalmente não são difíceis de encontrar - mesmo em uma linguagem totalmente dinâmica, o tipo errado em uma estrutura de dados geralmente causa um erro de execução muito óbvio, e o TDD quase sempre captura esse tipo de erro.

  • Imutabilidade - muitos erros graves são causados ​​por interações complexas de estado mutável. Idiomas que enfatizam a imutabilidade (Haskell, Clojure, Erlang) têm uma enorme vantagem ao evitar a mutabilidade

  • Programação funcional - as abordagens funcionais para escrever código tendem a ser mais "comprovadamente corretas" do que o código orientado a objetos, com sequências complexas de efeitos / interações. Minha experiência é que o FP ajuda a evitar erros complicados - acredito que há alguma pesquisa acadêmica em algum lugar que atualmente não consigo encontrar que apóie isso.

  • Suporte à simultaneidade - problemas de simultaneidade são particularmente difíceis de detectar e depurar, e é por isso que isso é tão importante. Qualquer coisa que exija bloqueio manual está fadada ao fracasso (e isso inclui praticamente todas as abordagens orientadas a objetos da concorrência). O melhor idioma que conheço a esse respeito é o Clojure - ele possui uma abordagem única para gerenciar a simultaneidade que combina memória transacional de software com estruturas de dados imutáveis ​​para obter uma nova estrutura de simultaneidade, confiável e componível. Consulte http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey para obter mais informações


Pessoas como eu, que são apoiadores apaixonados da programação funcional, apreciam essa resposta. Expressividade é sua amiga.
Sridhar Sarnobat

11
Melhor resposta até agora! Eu acho que isso é apoiado pelas duas análises a seguir sobre a frequência de erros: deliberate-software.com/safety-rank-part-2 e macbeth.cs.ucdavis.edu/lang_study.pdf . Ambos mostram que quanto mais pura, mais funcional, mais expressiva e mais segura a linguagem, menos bug ela possui. Da mesma forma, ambos mostram que Clojure e Ruby escapam da regra de Segurança, provavelmente indicando que a interatividade tem um impacto igual, como você apontou.
Didier A.

@DidierA. infelizmente o UCDavis ligação é interrompida (sem arquivo wbm) - Eu acho que você tem que partir página do Prem Devanbu que agora aponta para um link atualizado: Um Estudo em larga escala de linguagens de programação e código de qualidade no Github
icc97

5

Quanto menos poderosa é a linguagem, menos opções ela oferece para atirar no próprio pé.

Linguagens de alto nível como Java e C # produzirão menos erros do que linguagens de baixo nível como C ++.

Dito isto, acredito que o Java é mais seguro que o C #. O Java é artificialmente limitado para que um programador comum, sem conhecimento avançado, possa dominá-lo e produzir programas estáveis.


4
+1 em "Quanto menos poderosa for a linguagem, menos opções ela oferece para que você atire no próprio pé".
Michael K

O gráfico do missingfaktor parece dizer que C # e C ++ estão em pé de igualdade na medida em que são capazes de se atirar lá e eleva o Java acima disso. Não que eu concorde com essa parte do gráfico, no entanto. Você é forçado a passar por aros para fazer algumas cenas em C #.
Jesse C. Slicer

6
Segurança e poder não são inversamente proporcionais (que é o que você parece pensar ;-) Haskell, por exemplo, é EXTREMAMENTE poderoso e ainda tem muito poucas maneiras de se dar um tiro no pé.
missingfaktor

3
Se o idioma é fraco, você só precisa de um pé maior.

7
@missingfaktor, isso é porque é um efeito colateral dar um tiro no próprio pé.

3

Qual idioma, na sua opinião, permite que o programador médio produza recursos com a menor quantidade de bugs difíceis de encontrar?

Na minha opinião, Delphi. Baseada no Pascal, a linguagem é simples e intuitiva o suficiente para que o programador médio (ou mesmo os codificadores inexperientes) possam entender facilmente, e seu rico suporte de ferramentas e bibliotecas facilita a localização de muitos bugs.

  • Digitação forte e um compilador rigoroso que captura muitos erros comuns.
  • Sintaxe intuitiva que não incentiva erros comuns. ("O último bug do mundo" if (alert = RED) {LaunchNukes;}, não será compilado, por exemplo.)
  • Um modelo de objeto bem projetado que elimina muitos dos erros comuns de C ++ OOP.
  • Verificação de limites e verificação de alcance incorporada ao idioma, reduzindo drasticamente as chances de problemas de segurança.
  • Provavelmente, o compilador mais rápido conhecido pelo homem, o que aumenta sua produtividade e dificulta a perda de sua linha de pensamento enquanto espera uma compilação.
  • O depurador O depurador do Visual Studio deseja ser como quando crescer.
  • O rastreamento de vazamentos foi criado diretamente no gerenciador de memória, tornando trivial encontrar e corrigir vazamentos de memória.
  • Uma biblioteca padrão grande e madura, que fornece maneiras pré-construídas e pré-testadas para realizar tarefas comuns sem precisar criar suas próprias implementações, possivelmente com bugs.
  • É fornecido com ferramentas úteis, como um poderoso sistema de registro e um criador de perfil, para facilitar o rastreamento de problemas.
  • Forte suporte da comunidade para problemas comuns que não estão na biblioteca padrão, incluindo uma poderosa biblioteca de simultaneidade de terceiros .

Eu sou um jóquei em Delphi desde o início, mas ele se afastou das raízes de Pascal quando me permitiu escrever qualquer coisa para qualquer outra coisa, a la C / C ++:var I: Integer; Pointer(I)^ := $00;
Jesse C. Slicer

11
@ Jessé: Talvez, mas eu vejo isso como uma concessão necessária ao pragmatismo. Kernighan fez muitos pontos positivos quando escreveu Why Pascal não é minha linguagem de programação favorita. As previsões são necessárias para realizar muitas coisas importantes de baixo nível. Mas um dos pontos fortes do Delphi é a maneira como suas bibliotecas encapsulam detalhes de baixo nível e tornam desnecessárias a maior parte do ponteiro inseguro e do material tipográfico.
Mason Wheeler

Não discordo de que seja necessário - mas afirmar que a Digitação forte é um pouco negada por isso. O original Pascal não permitia tais travessuras e, portanto, foi fortemente tipado. Mas eu não chegaria tão longe a chamar Delphi de digitação fraca - é uma espécie de "digitação média-boa".
Jesse C. Slicer

11
@ Jessé: A versão original do Pascal para Wirth não permitia registros de variantes? Com o IIRC, eles acabaram se tornando tão comumente usados ​​para subverter a digitação forte que Borland e outros decidiram simplesmente inserir previsões para simplificar, porque todo mundo estava fazendo isso de qualquer maneira.
Mason Wheeler

en.wikipedia.org/wiki/Pascal_(programming_language)#Divisions e en.wikipedia.org/wiki/Pascal_(programming_language)#Criticism , bem como pascal-central.com/ppl/chapter3.html, parecem indicar que fazia parte de o primeiro padrão em 1983. Vejo algumas referências de Wirth que parecem datam de 1974, então eu diria que sim. Eu acho que a parte problemática foi permitir que ela fosse subvertida como tal (os campos variantes que tomam a mesma memória, como uniões em C). Se eles fossem simplesmente usados ​​como mecanismo de escopo e o layout da memória fosse o superconjunto, seria mais forte o tipo.
Jesse C. Slicer

2

Uma coisa a levar em consideração é a mudança de direção.

Nos últimos cinco anos, desenvolvi principalmente aplicativos da Web em java (JSF, Seam, etc.). Recentemente, consegui um novo emprego e estamos usando o Perl (com Catalyst e Moose).

Sou muito mais produtivo em Perl do que em Java.

Não é necessário compilar e implantar (quente), é um dos motivos. Também acho que escrever casos de uso é mais fácil, pois isso pode ser feito de maneira mais iterativa. E as estruturas em Java parecem ser desnecessárias e complexas, pelo menos para os projetos nos quais estive envolvido.

Eu acho que o número de bugs no meu código Perl é mais ou menos o mesmo que o número de bugs no meu código Java, pode até ser maior. Mas acho mais fácil e rápido encontrar e corrigir esses erros.


1

Talvez o levantamento do número de ferramentas disponíveis para análise de código estático e dinâmico para cada linguagem de programação possa dar uma idéia. Quanto mais ferramentas para um idioma, é mais provável que o idioma seja muito popular entre os usuários ou muito popular na geração de bugs difíceis de encontrar. Mas não consigo convencer o Google a fazer nenhum estudo sobre esse assunto. Também deve ser observado que alguns idiomas, como C, podem ser usados ​​para solucionar os bugs de hardware subjacentes, bem como para solucionar o desgaste do hardware à medida que envelhece.


11
"contornar o desgaste do hardware à medida que envelhece" ...?
Jrandom_hacker

Eu li que alguns sistemas operacionais Unix executados em máquinas de missão crítica verificam a integridade da CPU, RAM e outro hardware. serverfault.com/questions/56192/… discute sobre isso com alguma profundidade. Se algumas linhas em um módulo de RAM ficarem defeituosas com o tempo, esses módulos defeituosos não serão usados ​​pelo sistema operacional e não os reportarão na memória física total disponível. Essas coisas também podem ser feitas em outros hardwares.
precisa saber é o seguinte

É um boato interessante, mas não vejo como é relevante aqui. Além disso, nada no seu link menciona esses sistemas operacionais Unix auto-reparáveis ​​- ele apenas fala sobre maneiras de testar o hardware de um PC.
Jrandom_hacker

11
Eu mencionei que isso significa que os programas por si só podem não ser as fontes de bugs, podem ser também o hardware ou outros fatores externos.
precisa saber é o seguinte

1

Em vez de falar sobre idiomas, que tal falar sobre recursos de idioma

  • java obriga a pensar em exceções (lançamentos ...) e você deve publicar ou manipular essas exceções. Isso realmente me impede de esquecer as situações de erro ou estou usando mais exceções derivadas do SystemException que não precisam desse tratamento?
  • o que dizer de "design by contract" (http://en.wikipedia.org/wiki/Design_by_contract) que me obriga a pensar em pré e pós-condições. Eu li que agora é possível com c # -4.0.
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.