2 pontos por GN⁺ 2024-08-06 | 1 comentários | Compartilhar no WhatsApp
  • A experiência de reescrever, ao longo de um mês, o núcleo de um programa que eu vinha usando e ajustando por 2 anos abalou minhas crenças anteriores sobre testes e controle de versão
  • Em 2015, eu via testes e versões como o núcleo de um software duradouro, mais do que abstrações ruins; mas, passando por Mu e Freewheeling Apps, meu modo real de trabalho foi mudando cada vez mais
  • Vejo que programas duradouros devem ser feitos não para muita gente, mas dentro de pessoas, contextos e funcionalidades que se conhece bem, aceitando limites realistas como o Dunbar's number
  • Tipos, abstrações, testes, versões, máquinas de estado, imutabilidade e análise formal são úteis em áreas desconhecidas, mas, em excesso, viram dívida técnica que encobre complexidade desnecessária
  • Quando a compreensão do contexto se estabiliza, vale a pena descartar grandes partes e refazê-las; é preciso carregar de uma só vez na cabeça os cenários necessários e então compor o todo de uma vez

Mudança de visão sobre testes e controle de versão

  • Venho lidando continuamente com a questão de escolher e criar pessoalmente programas dos quais eu possa depender por muito tempo, mas eu mesmo não sinto que faça isso bem
  • Recentemente, ao longo de um mês, reescrevi o núcleo de um programa que eu vinha usando e modificando gradualmente por 2 anos
    • Depois disso, passei alguns dias organizando o que aprendi e para onde ir em seguida
    • Esse trabalho começou a revelar uma mudança mais ampla na minha trajetória de vida
  • Em 2015, eu desconfiava de abstrações e dava importância a testes e controle de versão
    • Eu via muito código com abstrações ruins, e considerava testes e versões como o avanço central dos anos 2000
    • Eu atribuía as causas dos problemas a incentivos ruins, abstração excessiva e testes e versionamento insuficientes
    • Mu1 foi uma tentativa de projetar uma plataforma tomando testes e layers como restrições fundamentais
  • Em 2017, comecei a retrabalhar o Mu1 no Mu atual
    • No início, usei todas as novas ideias sobre testes e camadas
    • Com o tempo, passei a usar menos essas ideias
    • Hoje o Mu tem muitos testes, mas a maioria é de testes comuns, e eu não consegui portar a infraestrutura de camadas
  • Em 2022, comecei a criar Freewheeling Apps
    • No começo não havia testes; mais tarde, escrevi testes rigorosos para uma peça central, o editor de texto
    • Foi difícil encontrar uma forma de testar o restante, mas consegui avançar o suficiente sem testes
  • Em 2024, apaguei todos os testes
    • Comecei a refazer em grande escala o editor de texto, e essa abordagem poderia ter me levado a me preocupar com conflitos de merge com outros Freewheeling Apps
    • Como resultado, também parei de pensar em controle de versão
    • Depois de abrir mão de testes e versões e obter um programa melhor, ficou cada vez mais difícil ignorar a dissonância cognitiva com minhas crenças anteriores

Minha síntese atual sobre programas duradouros

  • Criar algo que dure muito tempo para muita gente é difícil demais, então acho melhor nem tentar fazer isso desde o início
    • É preciso ser governado pelo que se conhece bem, por pessoas que se conhece bem e pelo Dunbar's number
  • Vejo a maior parte do software do mundo como contaminada por incentivos para servir muita gente no curto prazo
    • Sempre que possível, concentro-me em softwares cujos sites não têm muitos logos
    • Prefiro softwares fáceis de criar, com poucas dependências e sem atualização automática
    • Ao filtrar com essas restrições, a quantidade de software duradouro que a humanidade criou até hoje é muito pequena
  • Pequenas mudanças de contexto — como pessoas, lugares e funcionalidades que se quer apoiar — podem mudar muito o quanto um programa se encaixa bem nesse contexto
    • Em um ambiente dominado pelo curto-prazismo, é difícil se preparar para esse fato
  • Como o volume de trabalho passado é pequeno e o escopo de aplicação de cada programa também é baixo, qualquer programa que eu decida criar provavelmente entrará em algum território desconhecido
    • Mesmo ao tentar colocar “drawing lines” especiais em um editor de texto, surgem várias perguntas
      • O cursor pode ficar sobre um desenho?
      • É possível desenhar em uma linha enquanto o cursor está em outra?
      • O desenho é mais alto que uma linha de texto; ele pode aparecer apenas parcialmente no topo da tela?
      • É possível desenhar sobre um desenho parcialmente visível?
    • Durante muito tempo, as respostas a essas perguntas não foram ideais, e gambiarras foram se acumulando sobre gambiarras

Ferramentas são necessárias, mas em excesso viram dívida técnica

  • Tipos, abstrações, testes, versões, máquinas de estado, imutabilidade e análise formal são ferramentas que podem ser usadas em terreno desconhecido
    • Basta usá-las na medida necessária e de acordo com o gosto de cada um
  • As pessoas tendem a usar em excesso as ferramentas pelas quais se sentem atraídas
    • Acredito que a quantidade ideal de uso dessas ferramentas é muito pequena
    • Deve ser muito menor do que a intuição aprendida em um ambiente dominado pelo curto-prazismo
  • O uso excessivo de ferramentas vira dívida técnica
    • Fica mais difícil perceber que o programa é desnecessariamente complexo
    • O programa passa a durar menos do que poderia
    • Fica mais difícil alterar o programa quando o contexto muda

Reescrever e “criar tudo de uma vez”

  • Quando a compreensão do contexto se estabiliza, há valor em jogar fora grandes partes do programa e recomeçar do zero
  • Antes de reescrever, é preciso trazer para a cabeça, de uma só vez, tudo o que se quer do programa e todos os cenários aos quais ele precisa responder
    • Esse processo é difícil, mas o objetivo é chegar a um estado em que seja possível criar tudo de uma vez
  • O modo final é criar tudo de uma vez
  • Nesta experiência, testes e versões acabaram atrapalhando a chegada ao fim dessa evolução
    • Testes fazem esquecer os problemas com os quais é preciso se preocupar
    • Controle de versão mantém a pessoa presa ao passado
    • Ambos foram contraproducentes, e abandoná-los exigiu uma grande mudança de direção
  • Vejo todos os softwares que criei até agora e os Freewheeling Apps como estando no estágio 6 dessa trajetória

Limites da complexidade e design orientado a dados

  • Se um programa ficar complexo demais, pode se tornar impossível, no estágio 8, carregá-lo inteiro na cabeça
    • Acredito que isso se aplica à maior parte do software já produzido, especialmente ao software escrito por mais do que uma ou duas pessoas
    • Até um pequeno editor de texto foi intimidador, e passei boa parte do mês me preparando para enfrentar esse medo
  • Nem todo software precisa necessariamente chegar ao estágio 9
    • Muitos Freewheeling Apps são suficientemente simples e evoluem devagar
    • Acredito que, só por serem usados por poucas pessoas, eles podem se estabilizar sem bugs independentemente das escolhas iniciais de design
    • Especialmente agora que sei como simplificar uma peça complexa do núcleo
  • Ainda assim, quando algo ganha valor, é bom saber como melhorá-lo
  • Aponto data-oriented design como um método que parece claramente útil para chegar ao estágio 9
    • Não é uma ferramenta que se possa aplicar cegamente, mas uma forma de pensar que enxerga em grande escala como o programa acessa os dados
    • Ferramentas como ECS não devem encobrir a atividade intelectual essencial
  • Essa divisão em estágios pode não estar totalmente correta
    • Posso estar subestimando ferramentas com as quais tenho pouca experiência
    • O que existe além desses estágios também continua sendo uma questão em aberto
  • É possível ver sinais de como meu pensamento mudou desde o texto sobre meu modo de programar que escrevi em 2019

1 comentários

 
GN⁺ 2024-08-06
Opiniões no Hacker News
  • Se não há testes, você não vê falhas nos testes, então o problema apenas parece ter desaparecido
    Nunca aconteceu de eu testar algo e não encontrar bugs, e a maior parte do que testei eram coisas que eu já achava que estavam prontas para serem lançadas
    Se você apagar os testes, é bem provável que a única pessoa que esteja enganando no fim seja você mesmo. Lendo o texto, parece que ele se cansou mais de gerenciamento de variantes/configuração do que dos testes em si, e isso é totalmente compreensível. Mas é preciso ter usuários para ganhar dinheiro, e, se fosse um problema fácil, o mercado já estaria saturado de soluções universais

    • Acho que isso é dependente do domínio. Em algumas partes da base de código em que estou trabalhando agora, os testes ajudam muito na refatoração, mas em outras há muito comportamento de UI, então testes manuais são muito mais rápidos
      Quando a UI ou o workflow muda rápido demais, você acaba não escrevendo testes porque sabe que eles serão inúteis na próxima iteração; por outro lado, quando muda devagar demais, você não vai mexer naquela parte de novo, então há pouca chance de introduzir novos bugs por refatoração. Testes e tipos não são um cálice sagrado universal, são ferramentas adequadas a cada trabalho. Nunca vi uma base de código em que, mesmo com boa cobertura de testes, bugs não fossem descobertos por testes manuais ou pelo uso real. Exagerando um pouco: se você é bom o bastante para escrever testes perfeitos, poderia simplesmente escrever código perfeito. Se não consegue escrever testes perfeitos, como saber que esses testes são completos, sem bugs e realmente úteis?
    • A frase “testes podem mostrar a presença de bugs, mas não podem mostrar sua ausência” combina melhor com a minha experiência
      A cada poucos meses eu encontrava um novo bug e adicionava testes diligentemente, mas alguns meses depois alguém que usava por 10 minutos pela primeira vez encontrava outro bug novo. Ainda haverá bugs a encontrar na nova versão, mas acho que, graças às estruturas de dados escolhidas, boa parte dos testes antigos deixou de ser estruturalmente necessária. Espero que, pelo menos para uso leve, ela fique bastante estável depois de corrigir mais alguns bugs. Testes são muito valiosos quando uma equipe grande continua alterando uma base de código, mas aqui a ideia é criar algo com um conjunto fixo de funcionalidades
    • Vi o vídeo Go Testing By Example, de Russ Cox, recomendado em uma thread recente: https://www.youtube.com/watch?v=X4rxi9jStLo
      Há muitos conselhos úteis, mas uma coisa que quero destacar em especial é que dá para testar contra uma implementação mais simples, por exemplo uma implementação por força bruta. Há uma sabedoria mais profunda nisso. A utilidade de um teste depende de quão mais simples a implementação do teste é em relação à implementação testada. Dizendo de forma mais forte: testes só são úteis quando são mais simples do que aquilo que testam. Por mais testes que você escreva, no fim ainda precisa raciocinar sobre o código, e o simples fato de algo ser um “teste” não o torna útil. Por isso acho que muitos programadores desconfiam de quebrar funções em pedaços que não são interfaces úteis só para facilitar testes, de testar helpers simples ou pequenas consultas apenas por cobertura, ou de introduzir inversão de dependência e mocks apenas por causa dos testes. Claro que pode haver motivos para cada uma dessas coisas, mas é importante não perder o ponto central
    • No meu caso, sempre que escrevi testes unitários, bugs apareceram
      Normalmente não sigo muito o estilo de desenvolvimento orientado a testes em que se escreve primeiro um teste que falha, embora às vezes faça isso. Então esses testes geralmente miram código que eu já achava que funcionava. Ainda assim, em geral prefiro um test harness a testes unitários[0]. Ele ainda encontra bugs, mas o fluxo é menos linear. Faz você testar mais durante o desenvolvimento e corrigir os bugs na hora
      [0] https://littlegreenviper.com/testing-harness-vs-unit/
    • O foco em testes automatizados unitários/de integração é uma tendência relativamente moderna, talvez desde o fim dos anos 90. Antes disso, softwares bem grandes e muito estáveis também eram lançados
      Por exemplo, o kernel Linux não tinha muitos testes antigamente, e hoje parece ter mais. O Unix provavelmente também não tinha muitos “testes”. Compiladores costumavam ter testes, mas sistemas operacionais menos, e jogos como Doom provavelmente também não tinham muitos testes. No fim, é preciso encontrar um ponto de equilíbrio. Sabemos que testes automatizados — isto é, unitários, de integração e de ponta a ponta — ajudam a criar software de qualidade. Ao mesmo tempo, bons testes nem sempre são fáceis de escrever, testes ruins dificultam refatorações, e testes instáveis consomem muito tempo em projetos grandes. Ainda assim, especialmente se você desenvolve sozinho, é interessante experimentar várias abordagens e descobrir o que funciona para você
  • A parte “quando abandonei testes e versões, o programa ficou muito melhor” é difícil de entender. Não sei quem, em 2024, escolheria programar voluntariamente sem controle de código-fonte
    Mesmo em um projeto de uma pessoa só, trabalhar em vários dispositivos, ver o histórico, reverter alterações e usar branches oferece um valor enorme por um custo quase nulo. Talvez eu tenha entendido mal o que o autor quis dizer com “versões”

    • Estou tentando criar algo pequeno, rápido e com um conjunto de funcionalidades fixo. Decidi construir em cima de uma base que não muda com frequência, e há mais contexto em https://akkartik.name/freewheeling
      É correto dizer que essa abordagem não se encaixa na maioria dos programas que as pessoas fazem hoje, isto é, com equipes grandes e requisitos que mudam constantemente. Ainda assim, continuo usando controle de código-fonte. Como disse no texto original, apenas parei de me preocupar em causar conflitos de merge com outros forks. Agora há mais de 24 forks, e os detalhes estão no link acima. Uso controle de versão para usos básicos como backup, “o que acabei de mudar?” e levar o software para um dispositivo novo. Só que, neste programa em específico, deixei de pensar no controle de versão como um meio de entender e acompanhar o que mudou. Há mais detalhes em https://akkartik.name/post/wart-layers. Por exemplo, passei a me preocupar menos com a higiene das mensagens de commit. O controle de versão existe, mas nesse contexto estreito — tentar criar um artefato durável, com conjunto de funcionalidades fixo e que dure décadas — ele caiu de prioridade como “boa prática de programação”
    • O autor não parece estar em uma situação em que precise dar suporte a usuários profissionais ou pagantes, e parece querer mais liberdade para experimentar do que garantir uma versão estável conhecida
      Também não parece estar lidando com um sistema grande ou com trabalho em equipe crítico. Nessas condições, as ferramentas podem não entregar tanto valor. Um flautista em uma grande orquestra tocando uma sinfonia complexa precisa de partitura e maestro, mas, se estiver tocando sozinho junto de uma drum machine ou fazendo free jazz, a partitura pode não ser muito necessária e até atrapalhar
    • O autor parece estar sofrendo de fadiga mental ou burnout em relação à programação. Se controle de versão incomoda a esse ponto, vejo isso como um sinal bastante bom de que é hora de descansar
    • Programadores são constantemente sobrecarregados por escolhas e opções. As ferramentas e o espírito da época costumam empurrar os “melhores recursos” na direção de tornar alguma coisa mais fácil
      Mas, quando há sempre 1000 opções fáceis, escolher a opção correta gera uma grande carga cognitiva. Esse também é um dos motivos pelos quais a indústria sacraliza todo tipo de boa prática e pressiona socialmente quem não as segue. Arquitetura ruim e código espaguete horrível tornam o trabalho muito difícil, mas questionar coisas que parecem obviamente certas e explorar ambientes de desenvolvimento restritivos, com menos opções e ferramentas, pode permitir mais foco no problema final. O controle de versão também incentiva branches a dividir o programa em “funcionalidades independentes”; o histórico leva ao uso cego de unidades funcionais que podem estar obsoletas; e a colaboração normalmente cristaliza no código fronteiras organizacionais irrelevantes. Isso também tem relação com o que Mel Conway disse. Os benefícios do controle de versão são senso comum, mas no nível de “resolver o problema de negócio X” existem trade-offs reais. É sugestivo que esses trade-offs sejam quase invisíveis no nível da indústria
    • Neste caso, parece que o autor se referia a codificar lógica de versão no próprio app. Por exemplo, endpoints de API versionados para compatibilidade retroativa
  • No começo achei que o autor estivesse completamente errado, mas ainda assim há alguns bons insights
    Esse fluxo de trabalho funciona muito bem para ele. A maioria de nós também consegue lembrar de ocasiões em que Git ou testes automatizados nos frustraram ou reduziram nossa produtividade. Há soluções mais simples e menos intrusivas, como fazer backup do código com Dropbox, FTP etc. O motivo de o método acima funcionar bem é que o autor está otimizando a própria produtividade em um projeto pessoal de estimação em que colabora com poucas pessoas. Testes automatizados são úteis, mas o autor parece gostar de criar programas pequenos o suficiente para que esse valor seja difícil de aparecer. Mesmo nesse contexto, acho que testes automatizados têm valor, mas todos podemos concordar que eles reduzem a velocidade. Claro que muita gente argumentará que a recompensa vem depois. Controle de versão e testes automatizados resolvem problemas reais. Hoje não faz sentido começar um projeto sem controle de versão, e há um motivo para testes automatizados serem uma boa prática. Ainda assim, no caso de uso específico do autor, parece razoável. Tirando as partes controversas sobre controle de versão/testes, os itens 7/8/9 capturam perfeitamente meu modo de pensar ao escrever e refatorar programas grandes. Escreve, joga fora, escreve de novo

    • Não concordo quanto ao controle de versão. Mesmo que seja um projeto solo e não haja vários branches de versões
      Pessoas cometem erros, e em um projeto com mais de 100 mil linhas é muito útil saber o que foi alterado nas últimas três semanas. Ajuda a encontrar e corrigir problemas. Um recurso ainda melhor são os branches, que permitem experimentar o que quiser mantendo uma forma de voltar ao último estado estável. Acho que dá para ficar sem testes automatizados
    • Mesmo em um projeto solo, vale muito a pena aprender Git o suficiente para configurar .gitignore e rodar algo como git init, git add -A, git commit -a -m "before I changed the foo function to use bar", de modo a poder voltar a uma revisão anterior
      Não é preciso dominar Git, mas só ter mensagens de commit e uma versão para a qual voltar já me salvou inúmeras vezes. Sem falar nos recursos mais avançados
  • É um texto bastante confuso. Fico realmente curioso para saber por que chegou ao primeiro lugar

    • Por um lado, pode ser o texto de um desenvolvedor experimentando outras ferramentas e técnicas para melhorar a própria vida. Por outro, pode ser bait para levar as pessoas a uma discussão
  • A principal motivação para ter um conjunto razoável de testes é reduzir a frustração. Um conjunto de testes dá ao desenvolvedor confiança para evoluir o sistema
    Quando bem feito, é comum também surgir algo parecido com: “foi difícil encontrar uma forma de testar o restante e, de todo modo, fomos nos virando bem”. À medida que a complexidade das funcionalidades cresce, a dificuldade de testar componentes ou o sistema inteiro pode se tornar difícil de administrar. Mas a filosofia de que abrir mão de testes e controle de versão resultou em um programa melhor não escala para além de uma pessoa. E mesmo assim só funciona quando essa pessoa conhece todas as decisões atuais e passadas contidas no código-fonte como uma memória recente e íntima. Além disso, se ela conhece a implementação em profundidade, toda validação de mudança, por definição, precisa ser feita manualmente

    • Há algum tempo vi no HN a história de uma pessoa que nunca fazia merge de código que não tivesse escrito ela mesma naquele dia
      Se, ao fim do dia, o código não estava em estado de merge, isso significava que ela não havia entendido o problema bem o suficiente para expressá-lo em um dia, então tentava de novo na manhã seguinte. Não sei se alguém se lembra disso, ou se estou confundindo com outro site ou anedota
    • Como equipe de programação de uma pessoa só, faz sentido. Sinceramente, mesmo trabalhando sozinho, só de pensar em programar sem um conjunto de testes ou controle de versão já dá medo
      Documentação, testes e controle de versão reduzem a quantidade de contexto do código que preciso manter na memória. Preciso lembrar os detalhes do código à minha frente, mas, se eu documento, testo e faço check-in explicando por que/como mudei, com uma boa mensagem de commit, posso tirar aquele código da cabeça e passar para a próxima tarefa
  • Um bom exemplo do item 3, de que “uma pequena mudança no contexto, como as pessoas/lugares/funcionalidades que você pretende atender, muda drasticamente o quanto o programa se encaixa nesse contexto”, é o K9 Mail. Ele agora está se tornando o Thunderbird para Android
    O K9 Mail começou com uma UI pouco tradicional que mostrava a lista de contas de e-mail na tela inicial e exibia a contagem de mensagens não lidas e de mensagens totais de cada conta. Havia uma caixa de entrada unificada, mas ela não era imposta ao usuário. Lembro que escolhi explicitamente esse app porque queria manter separadas uma conta pessoal, uma conta de trabalho e várias contas de trabalho fornecidas por clientes. Provavelmente muitos usuários do K9 escolheram o app pelo mesmo motivo. Foi por isso que houve muita insatisfação quando o desenvolvedor mudou para uma UI tradicional do Android, em que a lista de contas desliza a partir da esquerda e alternar entre contas exige um toque a mais. Se gostássemos desse tipo de UI, é bem provável que não tivéssemos escolhido o K9 em primeiro lugar. Então uma única mudança pequena — embora provavelmente com muito código por trás — arruinou a adequação do app aos usuários. Continuo usando a 5.600, a última versão com a UI antiga, e faço sideload dela toda vez que compro um aparelho novo. Mais peculiar ainda: para acessar as contas, uso apenas POP3. Meu fluxo é pré-visualizar no celular, apagar o que devo apagar, responder com cópia oculta para mim mesmo quando necessário e, no fim, baixar tudo no notebook; o K9 se encaixava perfeitamente nesse fluxo. Não preciso de nada sofisticado; um app no estilo dos anos 90 já basta

  • Continuo curioso para saber aonde esse caminho vai levar. Uma coisa é certa: fazer software sozinho é uma atividade completamente diferente de fazer software em equipe
    Sobre testes: testes são um meio, não um fim. Acho que o que buscamos é confiança. Quando estamos confiantes na implementação, testamos menos. Por outro lado, quando há algo que precisa necessariamente continuar funcionando, adicionamos alguns testes de integração nas fronteiras externas, que sofrem menos impacto de refatorações e desaceleram menos o ritmo. É como cutucar um backend web por fora, em vez de testar as partes internas. Testes unitários são bons para concretizar o design de uma nova API, mas, depois que a direção fica clara, esses testes se tornam quase inúteis

    • Há muitos bons motivos para ter testes mesmo em projetos de uma pessoa só
      Fixar temporariamente um if com algo como true || para chegar direto à funcionalidade que você está criando leva tempo e depois precisa ser removido. Basta criar e executar um teste, e ele pode ficar como teste de regressão. Se você está distribuindo um app grande ou lento — às vezes só o fato de usar Qt já faz o build ou a execução demorarem —, um teste isolado carrega mais rápido e executa mais rápido. Se reproduzir um bug leva 45 segundos, é melhor escrever um teste. Você automatiza a parte mais tediosa do trabalho, mantém o fluxo, pode verificar o estado do bug quantas vezes quiser sem ter de pensar toda vez se vale a pena conferir, e ele também pode ficar como teste de regressão
  • Gosto muito deste autor, e Mu é um dos meus projetos favoritos. É um projeto divertido, meio como uma máquina Lisp moderna, mas rodando no QEMU

  • Gosto da frase “a maior parte do software está irremediavelmente infectada por incentivos para servir muita gente no curto prazo”. Ela continua funcionando se você trocar software por “negócios”

  • Todos nós estamos, em alguma medida, sobrecarregados pela complexidade na área de engenharia de software. Às vezes, essa complexidade é acidental
    Mas não concordo que a solução seja rejeitar todas as ideias criadas ao longo de décadas. Por outro lado, também não devemos aceitar todas as soluções ao pé da letra nem usá-las “demais”. Estar sobrecarregado, por definição, acontece quando se usa algo em excesso. Escreva testes, use sistemas de controle de versão, use abstrações, mas é preciso saber por que você as usa. Quando esse “porquê” deixa de valer, é preciso reavaliar

    • Vejo a academia como uma das grandes fontes do problema. Sou examinador externo de estudantes de CS na Dinamarca, e eles ainda aprendem a criar abstrações upfront no estilo de orientação a objetos e arquitetura em cebola
      Isso é uma das piores receitas possíveis no desenvolvimento de software. Pior ainda é que essas coisas são ensinadas quase como religião. O estranho é que, nos últimos anos, a forma como profissionais escrevem software evoluiu bastante. Como eu disse, abstração não é intrinsecamente ruim para tudo. É até difícil imaginar dados típicos que vão para um banco SQL sem uma classe base com campos como updated e updated_by. Mas, em geral, quase não uso abstrações a menos que seja realmente obrigado. Só que, na academia, ainda ensinam exatamente o mesmo currículo que eu aprendi 25 anos atrás. É muito estranho avaliar alunos pela capacidade de criar enormes abstrações em UML bonitinho e implementá-las em código. 90% deles provavelmente nunca mais vão ver um único diagrama UML. Pelo menos na minha pequena área, será assim. Ainda assim, a realidade é a realidade
    • O único motivo pelo qual eu realmente comecei a usar Git foi o magit
      Eu gostaria que tudo tivesse uma “porcelana” de linha de comando nesse nível. Com uma saída padrão --help=ui e uma interface no estilo dialog, acho que daria para automatizar. Não é tanto uma questão de ficar sobrecarregado pela complexidade; é mais que há um limite para a quantidade de memória muscular ativa que conseguimos usar, e em algum ponto é preciso cortar