De cima para baixo é uma ótima maneira de descrever o que você sabe ou recriar o que já construiu.
O maior problema de cima para baixo é que muitas vezes simplesmente não há "topo". Você mudará de idéia sobre o que o sistema deve fazer ao desenvolver o sistema e ao explorar o domínio. Como pode ser o seu ponto de partida algo que você não sabe (ou seja, o que deseja que o sistema faça)?
Um "local" de cima para baixo é uma coisa boa ... alguns pensamentos à frente da codificação são claramente bons. Mas pensar e planejar demais não é, porque o que você está visualizando não é o cenário real (a menos que você já tenha estado lá antes, ou seja, se você não está construindo, mas reconstruindo). De cima para baixo global ao construir coisas novas é apenas um absurdo.
De baixo para cima deve ser (globalmente) a abordagem, a menos que você conheça 100% do problema, você precisa da codificação da solução conhecida e não se preocupa em procurar possíveis soluções alternativas.
A abordagem Lisp é o destilado de baixo para cima. Você não apenas constrói de baixo para cima, mas também pode moldar os tijolos da maneira que precisa. Nada é fixo, a liberdade é total. É claro que a liberdade assume responsabilidade e você pode fazer coisas horríveis usando mal esse poder.
Mas código horrível pode ser escrito em qualquer idioma. Mesmo em linguagens que são moldadas como gaiolas para a mente, projetadas com a esperança de que, com essas linguagens, até os macacos possam ter bons programas em funcionamento (uma idéia tão errada em tantos níveis que dói apenas pensar nisso).
Seu exemplo é sobre um servidor web. Agora, em 2012, este é um problema bem definido, você tem especificações a serem seguidas. Um servidor web é apenas um problema de implementação. Especialmente se você pretende escrever um servidor da Web substancialmente idêntico aos outros bilhões de servidores da Web que existem, nada é realmente claro, exceto algumas minúcias. Até o seu comentário sobre a RSA ainda está falando sobre um problema claramente definido, com especificações formais.
Com um problema bem definido, com especificações formais e soluções já conhecidas, a codificação está apenas se conectando nos pontos. De cima para baixo é bom para isso. Este é o paraíso do gerente de projetos.
Em muitos casos, no entanto, não há uma abordagem conhecida e comprovada a ser usada para conectar os pontos. Na verdade, muitas vezes é difícil dizer até quais são os pontos.
Suponha, por exemplo, que você seja solicitado a instruir uma máquina de corte automática a alinhar as peças a serem cortadas com um material impresso que não esteja perfeitamente em conformidade com o logotipo repetitivo teórico. Você recebe as peças e fotos do material tiradas pela máquina.
O que é uma regra de alinhamento? Você decide. O que é um padrão, como representá-lo? Você decide. Como alinhar as peças? Você decide. As peças podem ser "dobradas"? Depende, alguns não e outros sim, mas é claro que não muito. O que fazer se o material estiver muito deformado para que uma peça o corte de forma aceitável? Você decide. Todos os rolos de material são idênticos? Claro que não, mas você não pode incomodar o usuário para adaptar as regras de alinhamento para cada rolo ... isso seria impraticável. Quais fotos estão vendo as câmeras? O material, o que quer que isso signifique ... pode ser colorido, pode ser preto sobre preto, onde apenas o reflexo da luz torna o padrão evidente. O que significa reconhecer um padrão? Você decide.
Agora tente projetar a estrutura geral de uma solução para esse problema e faça uma cotação, em dinheiro e tempo. Minha aposta é que até a arquitetura do seu sistema ... (sim, a arquitetura) estará errada. A estimativa de custo e tempo serão números aleatórios.
Nós o implementamos e agora é um sistema operacional, mas mudamos de idéia sobre a própria forma do sistema várias vezes. Adicionamos subsistemas inteiros que agora não podem ser alcançados a partir dos menus. Mudamos de mestre / escravo em protocolos mais de uma vez. Provavelmente agora temos conhecimento suficiente para tentar reconstruí-lo melhor.
Outras empresas, é claro, resolveram o mesmo problema ... mas, a menos que você esteja em uma dessas empresas, provavelmente seu projeto detalhado de cima para baixo será uma piada. Podemos projetá-lo de cima para baixo. Você não pode porque nunca fez isso antes.
Provavelmente você também pode resolver o mesmo problema. Trabalhando de baixo para cima, no entanto. Começando com o que você sabe, aprendendo o que não sabe e somando.
Novos sistemas de software complexos são desenvolvidos, não projetados. De vez em quando alguém começa a projetar um novo e complexo sistema de software mal especificado do início (observe que, com um grande projeto de software complexo, existem apenas três possibilidades: a] a especificação é imprecisa, b] a especificação é errada e auto-contraditória ou c] ambos ... e na maioria das vezes [c] é o caso).
Esses são os projetos típicos de grandes empresas, com milhares e milhares de horas lançadas apenas nos slides do powerpoint e nos diagramas UML. Invariavelmente, eles falham completamente após queimar quantidades embaraçosas de recursos ... ou, em alguns casos muito excepcionais, eles finalmente entregam um software muito caro que implementa apenas uma pequena parte das especificações iniciais. E esse software invariavelmente é profundamente odiado pelos usuários ... não o tipo de software que você compraria, mas o tipo de software que você usa porque é forçado a fazê-lo.
Isso significa que eu acho que você deveria pensar apenas em codificar? Claro que não. Mas, na minha opinião, a construção deve começar do fundo (tijolos, código concreto) e deve subir ... e seu foco e atenção aos detalhes devem, em certo sentido, "desaparecer" à medida que você se afasta do que tem. De cima para baixo é frequentemente apresentado como se você colocasse o mesmo nível de detalhe para todo o sistema de uma só vez: mantenha-o dividindo todos os nós até que tudo fique óbvio ... nos módulos da realidade, o subsistema é "cultivado" a partir de sub-rotinas. Se você não tiver uma experiência anterior no problema específico, seu design descendente de um subsistema, módulo ou biblioteca será horrível. Você pode criar uma boa biblioteca depois de saber quais funções colocar, e não o contrário.
Muitas das idéias do Lisp estão ficando mais populares (funções de primeira classe, fechamentos, digitação dinâmica como padrão, coleta de lixo, metaprogramação, desenvolvimento interativo), mas o Lisp ainda é hoje (entre os idiomas que conheço) bastante singular em como é fácil modelar o código para o que você precisa.
Parâmetros de palavras-chave, por exemplo, já estão presentes, mas, se não estiverem presentes, poderão ser adicionados. Eu fiz isso (incluindo verificação de palavras-chave em tempo de compilação) para um compilador Lisp de brinquedo que estou experimentando e não é preciso muito código.
Com o C ++, o máximo que você pode obter é um grupo de especialistas em C ++ dizendo que os parâmetros das palavras-chave não são tão úteis, ou uma implementação de modelo incrivelmente complexa, quebrada e com meio respaldo que na verdade não é tão útil. As classes C ++ são objetos de primeira classe? Não e não há nada que você possa fazer sobre isso. Você pode ter introspecção em tempo de execução ou tempo de compilação? Não e não há nada que você possa fazer sobre isso.
Essa flexibilidade de linguagem do Lisp é o que o torna ótimo para a construção de baixo para cima. Você pode criar não apenas sub-rotinas, mas também a sintaxe e a semântica da linguagem. E, em certo sentido, o próprio Lisp é ascendente.