Você está na página 1de 257

Machine Translated by Google

O que todo programador precisa saber sobre cognição

Felinne Hermans
Prefácio de Jon Skeet

TRIPULAÇÃO
Machine Translated by Google

Longo prazo
Curto prazo
Filtro memória
memória

Em formação
1 2 3
4

Trabalhando
memória

Uma visão geral dos três processos cognitivos que este livro abrange: STM, LTM e memória de
trabalho. As setas rotuladas 1 representam informações que chegam ao seu cérebro. As setas rotuladas
2 indicam as informações que prosseguem em seu STM. A seta 3 representa as informações que viajam
do STM para a memória de trabalho, onde são combinadas com as informações do LTM (seta 4).
A memória de trabalho é onde a informação é processada enquanto você pensa sobre ela.
Machine Translated by Google

O cérebro do programador
Machine Translated by Google
Machine Translated by Google

O cérebro do programador
O QUE TODO PROGRAMADOR PRECISA SABER SOBRE COGNIÇÃO

FELIENNE HERMANS

PREFÁCIO DE JON SKEET

TRIPULAÇÃO

ILHA ABRIGO
Machine Translated by Google

Para obter informações on-line e encomendar este e outros livros de Manning, visite
www.manning.com. A editora oferece descontos neste livro quando encomendado em quantidade.
Para mais informações por favor entre em contato

Departamento de vendas especiais


Manning Publications Co.
Estrada Baldwin 20
Caixa Postal 761
Shelter Island, NY 11964
E-mail: pedidos@manning.com

©2021 por Manning Publications Co. Todos os direitos reservados.

Nenhuma parte desta publicação pode ser reproduzida, armazenada em sistema de recuperação ou transmitida, de
qualquer forma ou por meios eletrônicos, mecânicos, fotocópias ou outros, sem autorização prévia por escrito do editor.

Muitas das designações usadas por fabricantes e vendedores para distinguir seus produtos são reivindicadas como marcas
registradas. Onde essas designações aparecem no livro, e a Manning Publications estava ciente de uma reivindicação de
marca registrada, as designações foram impressas em maiúsculas iniciais ou todas em maiúsculas.

Reconhecendo a importância de preservar o que foi escrito, é política da Manning ter os livros que publicamos impressos em
papel sem ácido, e envidamos nossos melhores esforços para isso.
Reconhecendo também nossa responsabilidade de conservar os recursos de nosso planeta, os livros de Manning são
impressos em papel que é pelo menos 15% reciclado e processado sem o uso de cloro elementar.

O autor e o editor fizeram todos os esforços para garantir que as informações contidas neste livro estivessem corretas no
momento da impressão. O autor e o editor não assumem e, por meio deste, se isentam de qualquer responsabilidade
perante qualquer parte por qualquer perda, dano ou interrupção causada por erros ou omissões, sejam esses erros ou
omissões resultantes de negligência, acidente ou qualquer outra causa, ou do uso de as informações aqui contidas.

Manning Publications Co. Editor de desenvolvimento: Tricia Louvar


20 Baldwin Road PO Editor de desenvolvimento técnico: Jerry Kuch
Box 761 Shelter Island, Editor de revisão: Mihaela Batinic´
NY 11964 Editor de produção: Keri Hales

Editor de texto: Michele Mitchell


Revisor: Melody Dolab
Revisor técnico: Sébastien Portebois
Compositor: Gordan Salinovic
Designer de capa: Marija Tudor

ISBN 9781617298677

Impresso nos Estados Unidos da América


Machine Translated by Google

breve conteúdo
PARTE 1 NA MELHOR LEITURA DO CÓDIGO ................................................... .....1

1 ÿ Decodificando sua confusão ao codificar 3 2 ÿ


Leitura rápida para código 13 3 ÿ Como aprender a
sintaxe de programação rapidamente 33 4 ÿ Como ler
código complexo 46

PARTE 2 PENSANDO EM CÓDIGO................................................... ..65

5 ÿ Alcançando uma compreensão mais profunda do


código 67 6 ÿ Melhorando na resolução de problemas de
programação 91 7 ÿ Equívocos: Erros no pensamento 110

PARTE 3 SOBRE ESCREVER MELHOR CÓDIGO................................................. .125

8 ÿ Como melhorar a nomeação das coisas 127 9


ÿ Evitando código ruim e carga cognitiva: dois
frameworks 147
10 ÿ Melhorando na resolução de problemas complexos 160

v
Machine Translated by Google

vi BREVE CONTEÚDO

PARTE 4 SOBRE COLABORAR NO CÓDIGO ..................................... 177


11 ÿ O ato de escrever código 179
12 ÿ Projetando e melhorando sistemas maiores 191
13 ÿ Como integrar novos desenvolvedores 205
Machine Translated by Google

conteúdo
prefácio xiii
prefácio xv
agradecimentos xvii sobre
este livro xix
sobre o autor xxii
sobre a ilustração da capa xxiii

PARTE 1 NA MELHOR LEITURA DO CÓDIGO ........................................1

1 Decodificando sua confusão enquanto codifica 3


1.1 Diferentes tipos de confusão no código 4
Tipo de confusão 1: Falta de conhecimento 5 ÿ Tipo de confusão 2: Falta
de informação 5 ÿ Tipo de confusão 3: Falta de poder de processamento 6

1.2 Diferentes processos cognitivos que afetam a codificação 6 LTM e


programação 7 ÿ STM e programação 7 Memória de trabalho e
programação 8

1.3 Processos cognitivos em colaboração 9 Uma breve


dissecação de como os processos cognitivos interagiram 9
Processos cognitivos em relação a tarefas de programação 10

vii
Machine Translated by Google

viii CONTEÚDO

2 Velocidade de leitura para o código 13


2.1 Leitura rápida do código 14
O que acabou de acontecer no seu cérebro? 15 ÿ Reexamine sua
reprodução 16 ÿ Reexamine sua segunda tentativa de reproduzir código 17
ÿ Por que ler um código desconhecido é difícil? 18

2.2 Superando os limites de tamanho em sua memória 18


O poder do agrupamento 19 ÿ Programadores experientes podem se lembrar de
código melhor do que iniciantes 22

2.3 Você vê mais código do que pode ler 23


Memória icônica 23 ÿ Não é o que você lembra; é a maneira como você se lembra
25 ÿ Pratique o agrupamento 30

3 Como aprender a sintaxe de programação rapidamente 33


3.1 Dicas para lembrar a sintaxe 34
As interrupções causam estragos no seu fluxo de trabalho 34

3.2 Como aprender sintaxe rapidamente com flashcards 35


Quando usar os flashcards 36 ÿ Expandindo o conjunto de flashcards
36 ÿ Afinando o conjunto de flashcards 36

3.3 Como não esquecer as coisas 37


Por que esquecemos as memórias? 38 ÿ Repetição espaçada 39

3.4 Como lembrar a sintaxe por mais tempo 40


Duas formas de lembrar de informações 40 ÿ Apenas ver informações
não é suficiente 41 ÿ Lembrar de informações fortalece as memórias 42 ÿ
Fortalece as memórias pensando ativamente 42

4 Como ler código complexo 46


4.1 Por que é difícil entender código complexo 47
Qual é a diferença entre memória de trabalho e STM? 48
Tipos de carga cognitiva relacionados à programação 49

4.2 Técnicas para reduzir a carga cognitiva 51


Refatorando 51 ÿ Substituindo construções de linguagem desconhecidas 52
Sinônimos de código são ótimas adições a um baralho de flashcard 55

4.3 Auxílios de memória para usar quando sua memória de trabalho


está sobrecarregada 56

Criando um gráfico de dependência 56 ÿ Usando uma tabela de estado 59


Combinando gráficos de dependência e tabelas de estado 61
Machine Translated by Google

CONTEÚDO ix

PARTE 2 PENSANDO EM CÓDIGO ........................65

5 Alcançando uma compreensão mais profunda do código 67


5.1 Funções da estrutura de variáveis 68
Variáveis diferentes fazem coisas diferentes 68 ÿ Onze papéis para cobrir quase
todas as variáveis 69

5.2 Papéis e paradigmas 71


Benefícios das funções 72 ÿ Notação húngara 73

5.3 Aprofundando o conhecimento dos programas 75


Conhecimento do texto versus conhecimento do plano 75 ÿ Diferentes
estágios de compreensão do programa 76

5.4 A leitura do texto é semelhante à leitura do código 79


O que acontece no cérebro quando lemos código? 79 ÿ Se você pode aprender
francês, pode aprender Python 81

5.5 Estratégias de compreensão de texto aplicadas ao código 84


Ativando o conhecimento prévio 84 ÿ Monitorando 85 ÿ Determinando a importância
de diferentes linhas de código 86 ÿ Inferindo o significado dos nomes das variáveis
87 ÿ Visualizando 88 ÿ Questionando 89
Resumindo o código 90

6 Melhorando na resolução de problemas de programação 91


6.1 Usando modelos para pensar no código 92
Os benefícios de usar os modelos 92

6.2 Modelos mentais 94


Examinando modelos mentais em detalhes 96 ÿ Aprendendo novos modelos
mentais 97 ÿ Como usar modelos mentais de forma eficiente ao pensar em código
97

6.3 Máquinas nocionais 102


O que é uma máquina nocional? 103 ÿ Exemplos de máquinas nocionais
103 ÿ Diferentes níveis de máquinas nocionais 105

6.4 Máquinas nocionais e linguagem 106


Expandindo conjuntos de máquinas nocionais 106 ÿ Diferentes máquinas
nocionais podem criar modelos mentais conflitantes 107

6.5 Máquinas nocionais e esquemas 108


Por que os esquemas são importantes 108 ÿ As máquinas nocionais
são semânticas? 109
Machine Translated by Google

x CONTEÚDO

7 Equívocos: Erros no pensamento 110


7.1 Por que aprender uma segunda linguagem de programação é mais fácil
do que aprender a primeira 111
Como aumentar as chances de se beneficiar do conhecimento de programação
existente 113 ÿ Diferentes formas de transferência 114 ÿ Já sabendo alguma coisa:
Maldição ou bênção? 115 ÿ As dificuldades de transferência 116

7.2 Equívocos: Erros no pensamento 117


Depurando equívocos com mudança conceitual 118
Suprimindo equívocos 119 ÿ Equívocos sobre linguagens de programação
120 ÿ Evitando equívocos ao aprender uma nova linguagem de programação 122
ÿ Diagnosticando equívocos em uma nova base de código 122

PARTE 3 SOBRE ESCREVENDO MELHOR CÓDIGO ..............................125

8 Como ficar melhor em nomear as coisas 127


8.1 Por que nomear é importante 128
Por que nomear é importante 129 ÿ Diferentes perspectivas sobre
nomeação 129 ÿ As práticas iniciais de nomeação têm um impacto
duradouro 131

8.2 Aspectos cognitivos da nomeação 133


A formatação de nomes suporta seu STM 133 ÿ Nomes claros ajudam seu LTM
134 ÿ Os nomes de variáveis podem conter diferentes tipos de informações para
ajudá-lo a compreendê-los 135 ÿ Quando avaliar a qualidade dos nomes 136

8.3 Que tipos de nomes são mais fáceis de entender? 137


Abreviar ou não abreviar? 137 ÿ Caixa de cobra ou caixa de camelo? 140

8.4 A influência dos nomes nos bugs 141


Código com nomes ruins tem mais bugs 141

8.5 Como escolher nomes melhores 142


Moldes de nome 142 ÿ Modelo de três etapas de Feitelson para melhores nomes
de variáveis 145

9 Evitando código ruim e carga cognitiva: duas estruturas 147


9.1 Por que código com cheiro de código cria muita carga cognitiva 148
Uma breve introdução aos cheiros de código 148 ÿ Como cheiros de código
prejudicam a cognição 151
Machine Translated by Google

CONTEÚDO XI

9.2 A influência de nomes ruins na carga cognitiva 153


Antipadrões linguísticos 154 ÿ Medindo a carga cognitiva 155
Antipadrões linguísticos e carga cognitiva 158 ÿ Por que antipadrões linguísticos
causam confusão 159

10 Melhorando na resolução de problemas complexos 160


10.1 O que é resolução de problemas? 161
Elementos de resolução de problemas 161 ÿ Espaço de estado 161

10.2 Qual é o papel do LTM quando você resolve problemas de


programação? 162
A resolução de problemas é um processo cognitivo por si só? 162 ÿ Como ensinar
seu LTM a resolver problemas 164 ÿ Dois tipos de memórias que desempenham
um papel na resolução de problemas 164

10.3 Automatização: Criando memórias implícitas 167


Memórias implícitas ao longo do tempo 168 ÿ Por que a automação fará você
programar mais rápido 170 ÿ Melhorando as memórias implícitas 171

10.4 Aprendendo com o código e sua explicação 172


Um novo tipo de carga cognitiva: carga alemã 173 ÿ Usando exemplos trabalhados
em sua vida profissional 175

PARTE 4 SOBRE COLABORANDO NO CÓDIGO ..............................177

11 O ato de escrever o código 179


11.1 Diferentes atividades durante a programação 180
Pesquisando 180 ÿ Compreensão 181 ÿ Transcrição 181
Incrementação 181 ÿ Exploração 182 ÿ E quanto à depuração? 182

11.2 Programador interrompido 183


As tarefas de programação requerem um aquecimento 183 ÿ O que acontece após
uma interrupção? 184 ÿ Como se preparar melhor para interrupções 185
Quando interromper um programador 187 ÿ Alguns pensamentos sobre
multitarefa 189

12 Projetando e melhorando sistemas maiores 191


12.1 Examinando as propriedades das bases de código 192
Dimensões cognitivas 192 ÿ Usando o CDCB para melhorar sua base de
código 200 ÿ Manobras de design e seus trade-offs 201

12.2 Dimensões e atividades 202


Impacto das dimensões em diferentes atividades 202 ÿ Otimizando sua base de
código para atividades esperadas 204
Machine Translated by Google

xii CONTEÚDO

13 Como integrar novos desenvolvedores 205


13.1 Problemas no processo de integração 206
13.2 Diferenças entre especialistas e novatos 207
Comportamento dos iniciantes com mais profundidade 207 ÿ Diferença entre
ver conceitos de forma concreta e abstrata 211

13.3 Atividades para um melhor processo de integração 213


Limitar tarefas a uma atividade de programação 213 ÿ Suportar a memória do
onboardee 214 ÿ Ler o código em conjunto 216

13.4 Algumas palavras para fechar este livro 221

epílogo 221

índice 223
Machine Translated by Google

prefácio
Passei muito da minha vida pensando em programação, e se você está lendo este livro,
provavelmente também. Eu não gastei quase tanto tempo pensando em pensar, no entanto. O
conceito de nossos processos de pensamento e como interagimos com o código como humanos
tem sido importante para mim, mas não houve nenhum estudo científico por trás disso. Deixe-
me dar três exemplos.
Sou o principal contribuidor de um projeto .NET chamado Noda Time, fornecendo um
conjunto alternativo de tipos de data e hora para os embutidos no .NET. Tem sido um ótimo
ambiente para mim dedicar tempo ao design da API, principalmente no que diz respeito à
nomenclatura. Tendo visto os problemas causados por nomes que fazem parecer que alteram
um valor existente, mas na verdade retornam um novo valor, tentei usar nomes que fazem o
código com erros parecer errado quando você o lê. Por exemplo, o tipo LocalDate tem um
método PlusDays em vez de AddDays. Espero que este código pareça errado para a maioria dos desenvolvedo

data.MaisDias(1);

enquanto isso parece mais razoável:

amanhã = hoje.MaisDias(1);

Compare isso com o método AddDays no tipo .NET DateTime :

data.AdicionarDias(1);

xiii
Machine Translated by Google

xiv PREFÁCIO

Parece que está apenas modificando a data e não é um bug, embora seja tão incorreto quanto o
primeiro exemplo.
O segundo exemplo também é do Noda Time, mas não é tão específico. Considerando que muitas
bibliotecas tentam (por boas razões) fazer todo o trabalho duro sem que os desenvolvedores tenham
que pensar muito, explicitamente queremos que os usuários do Noda Time pensem bastante em seu
código de data e hora antecipadamente. Tentamos forçar os usuários a pensar no que eles realmente
estão tentando alcançar, sem ambiguidade - e então tentamos tornar mais fácil expressar isso
claramente no código.
Finalmente, há um exemplo conceitual de quais valores as variáveis mantêm em Java e C# e o que
acontece quando você passa um argumento para um método. Parece que tenho tentado contrariar a
noção de que objetos são passados por referência em Java durante a maior parte da minha vida e,
quando faço as contas, provavelmente é esse o caso. Suspeito que estou tentando ajudar outros
desenvolvedores a ajustar seus modelos mentais há cerca de 25 anos.
Acontece que a forma como os programadores pensam tem sido importante para mim há muito
tempo, mas sem nenhuma ciência por trás disso, apenas suposições e experiências duramente
conquistadas. Este livro ajuda a mudar isso, embora não seja exatamente o início desse processo para mim.
Conheci Felienne Hermans na conferência NDC em Oslo em 2017, quando ela fez sua apresentação
“Programação é escrita é programação”. Minha reação no Twitter diz tudo: “Preciso de muito tempo
para entender tudo. Mas uau. Uau." Eu vi Feli enne fazer essa apresentação (evoluindo com o tempo, é
claro) pelo menos três vezes agora e tirei algo novo dela a cada vez. Finalmente, havia algumas
explicações cognitivas para as coisas que eu vinha tentando fazer — e também algumas surpresas que
me desafiavam a ajustar minha abordagem.

Reações alternadas de “Ah, isso faz sentido agora!” e “Ah, eu não tinha pensado nisso!” foram o
ritmo de fundo ao ler este livro. Além de algumas sugestões práticas imediatas, como o uso de
flashcards, suspeito que o impacto do livro será mais sutil. Talvez seja um pouco mais de deliberação
sobre quando colocar uma linha em branco no código. Talvez seja uma mudança nas tarefas que damos
aos novos membros da equipe, ou mesmo apenas uma mudança no tempo dessas tarefas. Talvez seja
como explicamos conceitos no Stack Overflow.

Seja qual for o impacto, Felienne forneceu um tesouro de ideias para pensar e processar na
memória de trabalho e passar para a memória de longo prazo – pensar sobre pensar é viciante!

—JON SKEET
ENGENHEIRO DE RELAÇÕES DE EQUIPE DESENVOLVEDOR , GOOGLE
Machine Translated by Google

prefácio
Quando comecei a ensinar crianças a programar há cerca de 10 anos, rapidamente percebi que
não tinha a menor ideia de como as pessoas usam seus cérebros para qualquer coisa, especialmente
para programação. Embora eu tenha aprendido muito sobre programação na universidade, nenhum
curso em minha formação em ciência da computação me preparou para pensar sobre programação.

Se você seguiu um programa de ciência da computação como eu fiz, ou se você aprendeu a


programar sozinho, provavelmente não aprendeu sobre as funções cognitivas do cérebro. Portanto,
você também pode não saber como melhorar seu cérebro para ler e escrever código de uma
maneira melhor. Eu certamente não sabia, mas enquanto ensinava as crianças a programar, percebi
que precisava de uma compreensão mais profunda da cognição. Comecei então a aprender mais
sobre como pensamos e como aprendemos. Este livro é o resultado de meus últimos anos lendo
livros, conversando com pessoas e participando de palestras e conferências sobre aprendizado e
pensamento.
Compreender como seu cérebro funciona é interessante por si só, é claro, mas também é
importante para a programação. A programação é vista como uma das atividades cognitivas mais
exigentes que existem: você está resolvendo um problema de forma abstrata e manipulando um
programa, o que requer um nível de atenção que não vem naturalmente para a maioria das pessoas.
Perdeu um espaço? Erro. Calculou mal por onde começar a indexação do array? Erro. Não entendeu
o funcionamento preciso de um código já existente? Erro.
Há tantas maneiras pelas quais você pode dar um tiro no próprio pé durante a programação.
Como você verá neste livro, muitos dos erros que você comete estão enraizados em questões
cognitivas. Por exemplo, a falta de um espaço pode significar que você não domina a sintaxe da
linguagem de programação com detalhes suficientes. Calcular incorretamente onde indexar uma matriz pode

xv
Machine Translated by Google

xvi PREFÁCIO

indicam que você tem suposições erradas sobre o código. Incompreensão do código existente
pode ser causado por uma falta de habilidades em como ler código.
O objetivo deste livro é primeiro ajudá-lo a entender como seu cérebro processa o código.
Compreender o que seu cérebro faz quando apresentado a novas informações ajudará
você seja um programador melhor porque programadores profissionais se deparam com novos
informações com frequência. Assim que soubermos como o código afeta o cérebro, falaremos sobre métodos para
melhorar suas habilidades de processamento de código.
Machine Translated by Google

agradecimentos
Percebo perfeitamente como sou tremendamente sortudo por poder concluir um livro sobre um tópico que
Ame. Há tantas coisas na minha vida que aconteceram exatamente da maneira certa no
momento exato, sem o qual minha vida teria sido muito diferente, e este
livro certamente não teria sido escrito. Dezenas de pequenos e grandes encontros
com pessoas incríveis contribuíram para este livro e para minha carreira. eu quero
citar alguns muito importantes.
Marlies Aldewereld primeiro me colocou no caminho da programação e do aprendizado de
idiomas. Marileen Smit me ensinou psicologia suficiente para escrever este livro. Greg Wilson
tornou o tema da educação em programação mainstream novamente. Peter Nabbe e Rob
Hoogerwood deu exemplos de classe mundial de como ser um grande professor. Stefan
Hanenberg me deu conselhos que moldaram a trajetória de minha pesquisa. Katja Mordaunt deu
início ao primeiro clube de leitura de códigos. O pensamento de Llewellyn Falco sobre a forma de koans
meu pensamento em aprender extensivamente. E Rico Huijbers é meu farol em qualquer tempestade.
Além dessas pessoas, é claro, preciso agradecer ao pessoal da Manning...
Marjan Bace, Mike Stephens, Tricia Louvar, Bert Bates, Mihaela Batinic´, Becky Rein hart,
Melissa Ice, Jennifer Houle, Paul Wells, Jerry Kuch, Rachel Head, Sébastien Porte bois, Candace
Gillholley, Chris Kaufmann, Matko Hrvatin, Ivan Martinovic ´, Branko
Latincic, e Andrej Hofšuster por aceitar essa vaga ideia de livro e transformá-la em
algo legível e sensato.
A todos os revisores: Adam Kaczmarek, Adriaan Beiertz, Alex Rios, Ariel Gamiño, Ben
McNamara, Bill Mitchell, Billy O'Callaghan, Bruno Sonnino, Charles Lam, Claudia
Maderthaner, Clifford Thurber, Daniela Zapata Riesco, Emanuele Origgi, George

xvii
Machine Translated by Google

xviii AGRADECIMENTOS

Onofrei , George Thomas, Gilberto Taccari, Haim Raman, Jaume Lopez, Joseph Pere nia,
Kent Spillner, Kimberly Winston-Jackson, Manuel Gonzalez, Marcin Sÿk, Mark Harris, Martin
Knudsen, Mike Hewitson, Mike Taylor, Orlando Méndez Morales, Pedro
Seromenho, Peter Morgan, Samantha Berk, Sebastian Felling, Sébastien Portebois,
Simon Tschöke, Stefano Ongarello, Thomas Overby Hansen, Tim van Deurzen, Tuomo
Kalliokoski, Unnikrishnan Kumar, Vasile Boris, Viktor Bek, Zachery Beyel e Zhijun
Liu, suas sugestões ajudaram a tornar este livro melhor.
Machine Translated by Google

sobre este livro


O Cérebro do Programador é um livro para programadores de todos os níveis que desejam obter um
compreensão mais profunda de como seus cérebros funcionam e como eles podem melhorar suas habilidades
e hábitos de programação. Exemplos de código em várias linguagens serão mostrados, incluindo JavaScript,
Python e Java, mas você não precisa de conhecimento profundo de nenhum dos
desde que você se sinta à vontade para ler o código-fonte em linguagens de programação que talvez não
tenha visto antes.
Para tirar o máximo proveito da leitura deste livro, você deve ter experiência em trabalhar em um
equipe de desenvolvimento ou em sistemas de software maiores e integrando pessoas a uma equipe.
Faremos referência a esses tipos de situações com frequência, e você obterá uma compreensão mais
profunda se puder relacioná-las com suas próprias experiências. De fato, o aprendizado aumenta quando
você pode conectar novas informações ao conhecimento e experiências existentes, que eu
capa neste livro.

Embora este livro apresente muitos tópicos da ciência cognitiva, é, em última análise, um livro
destinado especificamente para programadores. Sempre contextualizaremos o funcionamento do cérebro
com resultados de estudos sobre programação e linguagens de programação especificamente.

Como este livro está organizado: um roteiro


Este livro é composto por 13 capítulos divididos em 4 partes. Os capítulos devem ser lidos em
ordem porque eles constroem uns sobre os outros. Cada capítulo oferece aplicações e exercícios que o
ajudarão a processar o material e entendê-lo mais profundamente. Em alguns
casos, pedirei que você encontre uma base de código para realizar os exercícios para garantir que o
contexto é melhor para você.

xix
Machine Translated by Google

xx SOBRE ESTE LIVRO

Sua prática diária também é um lugar onde o conhecimento deve ser aplicado. Imagino que você
possa ler este livro por um período prolongado e aplicar as lições de um capítulo à sua prática de
programação antes de passar a ler mais capítulos:
ÿ O Capítulo 1 examina os três processos cognitivos que desempenham um papel na
programação e como cada um está associado ao seu próprio tipo de confusão. ÿ O Capítulo
2 discute como ler código rapidamente e ter uma noção de seu funcionamento. ÿ O Capítulo 3
ensina como aprender a sintaxe e os conceitos de programação melhor e mais facilmente. ÿ O
Capítulo 4 ajuda você a ler códigos complexos. ÿ O Capítulo 5 mostra técnicas que ajudam
você a alcançar uma compreensão mais profunda

código desconhecido.

ÿ O Capítulo 6 aborda técnicas para melhorar a resolução de problemas de programação. ÿ


O Capítulo 7 ajuda a evitar erros no código e no pensamento. ÿ O Capítulo 8 discute como
selecionar nomes de variáveis claros, especialmente em um código
base.
ÿ O Capítulo 9 enfoca os cheiros de código e os princípios cognitivos por trás deles. ÿ O
Capítulo 10 discute técnicas mais avançadas para resolver problemas complexos. ÿ O
Capítulo 11 cobre o ato de codificação e explora a variedade de tarefas em
programação.
ÿ O Capítulo 12 ensina maneiras de melhorar grandes bases de código.
ÿ O Capítulo 13 ajuda você a tornar o processo de integração de novos desenvolvedores menos
doloroso.

Este livro contém muitos exemplos de código-fonte tanto em listagens numeradas quanto em linha
com o texto normal. Em ambos os casos, o código-fonte é formatado em uma fonte de largura fixa
como esta para separá-lo do texto comum. Às vezes, o código também está em negrito para realçar
o código que foi alterado em relação às etapas anteriores do capítulo, como quando um novo recurso
é adicionado a uma linha de código existente.
Em muitos casos, o código-fonte original foi reformatado; adicionamos quebras de linha e recuo
retrabalhado para acomodar o espaço de página disponível no livro. Em casos raros, mesmo isso não
foi suficiente, e as listagens incluem marcadores de continuação de linha (ÿ). Além disso, os
comentários no código-fonte geralmente são removidos das listagens quando o código é descrito no
texto. Anotações de código acompanham muitas das listagens, destacando conceitos importantes.

fórum de discussão ao vivo


A compra do The Programmer's Brain inclui acesso gratuito a um fórum privado da Web administrado
pela Manning Publications, onde você pode fazer comentários sobre o livro, fazer perguntas técnicas
e receber ajuda do autor e de outros usuários. Para acessar o fórum, acesse https://
livebook.manning.com/#!/book/the-programmers-brain/ discussão. Você também pode aprender mais
sobre os fóruns de Manning e as regras de conduta em https://livebook.manning.com/#!/discussion.
Machine Translated by Google

SOBRE ESTE LIVRO xxi

O compromisso da Manning com nossos leitores é fornecer um local onde um


diálogo entre leitores individuais e entre leitores e o autor pode tomar
Lugar, colocar. Não é um compromisso com qualquer quantidade específica de participação por parte do
o autor, cuja contribuição para o fórum permanece voluntária (e não remunerada). Sugerimos que você
tente fazer ao autor algumas perguntas desafiadoras para que seu interesse não se perca! O
fórum e os arquivos de discussões anteriores estarão acessíveis a partir do editor
site enquanto o livro estiver impresso.
Machine Translated by Google

Sobre o autor
DR. FELIENNE HERMANS é professora associada da Universidade de Leiden, na Holanda
onde pesquisa educação em programação e linguagens de programação. Ela também
é professor-educador na academia de professores da Vrije Universiteit Amsterdam,
especializando-se em didática da ciência da computação, e leciona no Lyceum Kralingen high
escola em Roterdã.
Felienne também é a criadora da linguagem de programação Hedy para
programadores iniciantes e é apresentadora do podcast Software Engineering Radio, um dos maiores
podcasts sobre software na web.

xxii
Machine Translated by Google

sobre a ilustração da capa


A figura na capa de The Programmer's Brain tem a legenda “femme Sauvage du Canada”,
ou mulher nativa do Canadá. A ilustração é retirada de uma coleção de trajes de vários
países de Jacques Grasset de Saint-Sauveur (1757-1810), intitulado Costumes civils
actuels de tous les peuples connus, publicado na França em 1788. Cada ilustração é
finamente desenhada e colorida por mão. A rica variedade da coleção de Grasset de Saint
Sauveur nos lembra vividamente de como as cidades e regiões do mundo eram
culturalmente separadas há apenas 200 anos. Isoladas umas das outras, as pessoas
falavam diferentes dialetos e línguas. Nas ruas ou no campo, era fácil identificar onde
moravam e qual era seu ofício ou posição na vida apenas pelo vestuário.
A forma como nos vestimos mudou desde então e a diversidade por região, tão rica na
época, desapareceu. Agora é difícil distinguir os habitantes de diferentes continentes,
muito menos de diferentes cidades, regiões ou países. Talvez tenhamos trocado a
diversidade cultural por uma vida pessoal mais variada – certamente por uma vida
tecnológica mais variada e acelerada.
Numa época em que é difícil distinguir um livro de computador do outro, Manning
celebra a inventividade e a iniciativa do negócio da informática com capas de livros
baseados na rica diversidade da vida regional de dois séculos atrás, ressuscitada por
Grasset de Saint -Fotos de Sauveur.

xxiii
Machine Translated by Google
Machine Translated by Google

Parte 1

Ao ler melhor o código

Ler código é uma parte essencial da programação, mas como um desenvolvedor profissional
oper, você pode não saber como. A leitura de código não é ensinada ou praticada com
frequência, e conhecer o código é confuso e muitas vezes um trabalho árduo. Os primeiros
capítulos deste livro ajudarão você a entender por que a leitura de código é tão difícil e o que
você pode fazer para melhorar nisso.
Machine Translated by Google
Machine Translated by Google

Decodificando sua confusão


enquanto codifica

Este capítulo abrange


ÿ Discriminando as diferentes maneiras pelas quais você
pode se confundir ao codificar
ÿ Comparar três processos cognitivos diferentes que
desempenham um papel na codificação
ÿ Compreender como os diferentes processos cognitivos se
complementam

A confusão faz parte da programação. Quando você aprende uma nova linguagem de programação,
conceito, ou estrutura, as novas ideias podem assustá-lo. Ao ler desconhecido
código ou código que você escreveu há muito tempo, você pode não entender o que o
código faz ou porque foi escrito do jeito que foi. Sempre que você começa a trabalhar em um novo
domínio de negócios, novos termos e jargões podem se chocar em seu cérebro.
Não é um problema ficar confuso por um tempo, é claro, mas você não quer ser
confuso por mais tempo do que o necessário. Este capítulo ensina você a reconhecer e decodificar
sua confusão. Talvez você nunca tenha pensado sobre isso, mas existem maneiras diferentes
estar confuso. Não saber o significado de um conceito de domínio é um tipo diferente de
confusão do que tentar ler um algoritmo complicado passo a passo.

3
Machine Translated by Google

4 CAPÍTULO 1 Decodificando sua confusão ao codificar

Diferentes tipos de confusão estão relacionados a diferentes tipos de processos cognitivos. Usando
vários exemplos de código, este capítulo detalhará três tipos diferentes de confusão e
explicar o que acontece em sua mente.
Ao final deste capítulo, você será capaz de reconhecer as diferentes maneiras que
código pode causar confusão e entender o processo cognitivo que está acontecendo em seu
cérebro em cada caso. Uma vez que você conheça os três tipos diferentes de confusão, e
os três processos cognitivos relacionados, os capítulos posteriores ensinarão como melhorar
esses processos cognitivos.

1.1 Diferentes tipos de confusão no código


Todo código desconhecido é confuso até certo ponto, mas nem todo código é confuso em
o mesmo caminho. Vamos ilustrar isso com três exemplos de código diferentes. Todos os três exemplos
traduzem um determinado número N ou n para binário. O primeiro programa é escrito em APL, o
a segunda em Java e a terceira em BASIC.
Dê a si mesmo alguns minutos para inspecionar profundamente esses programas. Em que tipo de
conhecimento você confia ao lê-los? Como isso difere para os três programas?
Você pode não ter as palavras neste momento para expressar o que acontece em seu cérebro
quando você lê esses programas, mas eu acho que vai parecer diferente para cada um. No
No final deste capítulo, você terá o vocabulário para discutir as diferentes
processos que ocorrem quando você lê o código.
O exemplo na listagem 1.1 é um programa convertendo o número n em uma representação binária em
APL. A confusão aqui reside no fato de que você pode não saber o que
T significa. A menos que você seja um matemático da década de 1960, provavelmente nunca
usou APL (uma linguagem de programação). Ele foi projetado especificamente para matemática
operações e quase não é usado em qualquer lugar hoje.

Listagem 1.1 Representação binária no APL

22222ÿn

O segundo exemplo é um programa convertendo o número n em uma representação binária em Java. A


confusão pode ser causada aqui por não saber sobre o funcionamento interno
de toBinaryString().

Listagem 1.2 Representação binária em Java

public class BinaryCalculator { public static void mian(Integer


n) {
System.out.println(Integer.toBinaryString(n));
}
}

O exemplo final é um programa convertendo o número N em uma representação binária


em BÁSICO. Este programa é confuso porque você não pode ver todos os pequenos passos que
estão sendo executados
Machine Translated by Google

Diferentes tipos de confusão no código 5

Listagem 1.3 Representação binária em BASIC

1 LET N2 = ABS (INT (N))


""
2 LET B$ =
3 PARA N1 = N2 PARA 0 PASSO 0
4 LET N2 = INT (N1 / 2)
5 LET B$ = STR$ (N1 - N2 * 2) + B$
6 DEIXE N1 = N2
7 PRÓXIMO N1

8 IMPRIMIR B$
9 DEVOLUÇÃO

1.1.1 Confusão tipo 1: Falta de conhecimento


Agora vamos mergulhar no que acontece quando você lê os três programas. Primeiro é o APL
programa. Veja como o programa está convertendo o número n em representação binária em
APL. A confusão aqui está no fato de que você pode não saber o que T significa.

Listagem 1.4 Representação binária no APL

22222ÿn

Estou assumindo que a maioria dos leitores deste livro não está tão familiarizada com o APL e não saberá
o significado do operador T. Portanto, a confusão aqui reside na falta de conhecimento.

1.1.2 Confusão tipo 2: Falta de informação


Para o segundo programa, a origem da confusão é diferente. Eu suponho que com
alguma familiaridade com programação, mesmo que você não seja um especialista em Java, seu cérebro
pode encontrar as partes relevantes do programa Java. Isso mostra um programa convertendo
número n em representação binária em Java. A confusão pode ser causada aqui por não
conhecer o funcionamento interno de toBinaryString().

Listagem 1.5 Representação binária em Java

public class BinaryCalculator { public static void mian(Integer


n) {
System.out.println(Integer.toBinaryString(n));
}
}

Com base no nome do método, você pode adivinhar a funcionalidade. No entanto, para aprofundar
entender o que o código faz, você precisaria navegar até a definição de
toBinaryString() em outro lugar no código e continue lendo lá. Então, o
problema aqui é a falta de informação. As informações sobre exatamente como o toBinary String()
funciona não estão prontamente disponíveis, mas precisam ser encontradas em outro lugar no
código.
Machine Translated by Google

6 CAPÍTULO 1 Decodificando sua confusão ao codificar

1.1.3 Tipo de confusão 3: Falta de poder de processamento


No terceiro programa, baseado nos nomes das variáveis e nas operações, você pode
fazer um palpite sobre o que o código faz. Mas se você realmente quer seguir
junto, você não pode processar toda a execução em seu cérebro. O programa convertendo o número N em
representação binária em BASIC é confuso porque você não pode
supervisionar todos os pequenos passos que estão sendo executados. Se você precisa entender todas as etapas, você
pode usar um auxílio de memória como valores intermediários de variáveis mostradas na figura 1.1.

1 LET N2 = ABS (INT (N))


2 LET B$ =
""
7
3 PARA N1 = N2 PARA 0 PASSO 0
4 LET N2 = INT (N1 / 2) 3
5
6
LET B$ = STR$ (N1 - N2 * 2) + B$
DEIXE N1 = N2
"1"
7 PRÓXIMO N1
8 IMPRIMIR B$ Figura 1.1
9 DEVOLUÇÃO
Representação binária em BASIC

A confusão aqui está relacionada à falta de poder de processamento. É muito difícil segurar todos os
valores intermediários das variáveis e as ações correspondentes em sua mente em
o mesmo tempo. Se você realmente deseja calcular mentalmente o que este programa faz, você
provavelmente usar uma caneta e papel para rabiscar alguns valores intermediários, ou até mesmo escrever
ao lado das linhas no snippet de código, conforme mostrado neste exemplo. Nestes três
programas, vimos que a confusão, embora sempre irritante e desconfortável, pode
tem três fontes diferentes. Em primeiro lugar, a confusão pode ser causada pela falta de conhecimento
a linguagem de programação, algoritmo ou domínio em mãos. Mas a confusão também pode ser
causado por não ter acesso total a todas as informações que você precisa para entender o código.
Especialmente porque o código hoje em dia geralmente usa várias bibliotecas, módulos e pacotes,
entender o código pode exigir uma navegação extensa na qual você precisa reunir novos
informações ao mesmo tempo que lembra o que você estava fazendo em primeiro lugar. Finalmente,
às vezes o código é mais complicado do que seu cérebro pode processar, e o que confunde
você é uma falta de poder de processamento.
Agora vamos mergulhar nos diferentes processos cognitivos que estão associados a cada um
esses três tipos de confusão.

1.2 Diferentes processos cognitivos que afetam a codificação


Vamos ampliar os três processos cognitivos diferentes que acontecem em seu cérebro
ao ler os três programas de exemplo. Conforme descrito, diferentes formas de confusão
estão relacionados a questões com diferentes processos cognitivos, todos relacionados à memória. Esses
são explicados no restante do capítulo com mais detalhes.
A falta de conhecimento significa que não há fatos relevantes suficientes em sua memória de longo
prazo (LTM), o local onde todas as suas memórias são armazenadas permanentemente. Uma falta
de informação, por outro lado, apresenta um desafio para sua memória de curto prazo
(STM). As informações que você está coletando devem ser armazenadas no STM temporariamente, mas se
Machine Translated by Google

Diferentes processos cognitivos que afetam a codificação 7

você tem que pesquisar em muitos lugares diferentes, você pode esquecer algumas das coisas que você
já li. Finalmente, quando você precisa processar muitas informações, isso afeta
a memória de trabalho, que é onde seu pensamento acontece.
Aqui está um breve resumo de como os diferentes tipos de confusão estão relacionados ao
processos cognitivos diferentes:

ÿ Falta de conhecimento = Problema no LTM


ÿ Falta de informação = Problema no STM

ÿ Falta de poder de processamento = Problema na memória de trabalho

Esses três processos cognitivos não estão apenas em jogo na leitura de código, mas em todas as
atividades cognitivas, incluindo (no contexto de programação) escrever código, projetar
a arquitetura de um sistema, ou escrever documentação.

1.2.1 LTM e programação


O primeiro processo cognitivo usado durante a programação é o LTM. Isso pode armazenar seu
memórias por muito tempo. A maioria das pessoas pode se lembrar de eventos que aconteceram anos ou
mesmo décadas atrás. Seu LTM desempenha um papel em tudo o que você faz, desde amarrar seu
cadarços, onde seus músculos lembram o que fazer quase automaticamente, para escrever um
busca binária, onde você lembra o algoritmo abstrato, a sintaxe da linguagem de programação e como
digitar no teclado. O Capítulo 3 detalhará o uso de
LTM com mais detalhes, incluindo essas diferentes formas de lembrar e maneiras de
fortalecer esse processo cognitivo.
Seu LTM armazena vários tipos de informações de programação relevantes. Pode, por
por exemplo, armazenar memórias de quando você aplicou com sucesso uma determinada técnica, o
significado de palavras-chave em Java, o significado de palavras em inglês ou o fato de maxint
em Java é 2147483647.
O LTM pode ser comparado ao disco rígido de um computador, guardando fatos por muito tempo
períodos de tempo.

PROGRAMA APL : LTM


Ao ler o programa em APL, o que você mais usa é o seu LTM. Se você sabe o significado
da palavra-chave APL ÿ, você a recuperará do LTM ao ler este programa.
O programa APL também ilustra a importância do conhecimento relevante de sintaxe. Se
você não sabe o que ÿ significa em APL, você terá muita dificuldade em entender
o programa. Por outro lado, se você sabe que ele representa a função de codificação diádica , que é uma
função que traduz um valor em uma representação numérica diferente, ler o programa é quase trivial.
Nenhuma palavra precisa ser entendida, e você
também não precisa descobrir o funcionamento do código passo a passo.

1.2.2 STM e programação


O segundo processo cognitivo envolvido na programação é o STM. Seu STM é usado para
reter brevemente as informações recebidas. Por exemplo, quando alguém lê um número de telefone
para você por telefone, ele não entra no seu LTM imediatamente. O numero do telefone
Machine Translated by Google

8 CAPÍTULO 1 Decodificando sua confusão ao codificar

primeiro vai para o seu STM, que tem um tamanho limitado. As estimativas diferem, mas a maioria dos cientistas
concordam que apenas alguns itens se encaixam no STM e certamente não mais do que uma dúzia.
Por exemplo, ao ler um programa, palavras-chave, nomes de variáveis e estruturas de dados usados são
armazenados temporariamente no STM.

PROGRAMA JAVA: STM

No programa Java, o maior processo cognitivo em jogo é o STM. Você primeira linha de processo
1 da listagem 1.6, que ensina que o parâmetro de entrada n da função é um inteiro. Nesse ponto, você não tem
certeza do que a função fará, mas pode continuar
lendo enquanto também lembrando que n é um número. O conhecimento de que n é um inteiro é armazenado em
seu STM por um tempo. Você então continua na linha 2, onde toBinaryS tring() indica a você o que a função
retornará. Você pode não se lembrar disso
funcionar em um dia, ou mesmo em uma hora. Quando seu cérebro resolveu o problema em
lado—neste caso, entendendo a função—o STM é esvaziado.

Listagem 1.6 Um programa convertendo o número n em representação binária em Java

public static void mian(Int n) {


System.out.println(Integer.toBinaryString(n));
}
}

A confusão pode ser causada aqui por não saber sobre o funcionamento interno de
toBinaryString().
Embora o STM desempenhe um grande papel na compreensão deste programa, o LTM é
envolvidos na leitura deste programa também. Na verdade, nosso LTM está envolvido em tudo o que fazemos.
Então, ao ler o programa Java, você também usa seu LTM.
Por exemplo, se você estiver familiarizado com Java, como presumo que a maioria dos leitores esteja, você sabe
que as palavras-chave public class e public static void main podem ser desconsideradas se você estiver
solicitado a explicar o que a função faz. É provável que você nem tenha notado que o
método é de fato chamado “mian” e não “main”.

Seu cérebro pegou um atalho assumindo um nome, mostrando uma mistura do


dois processos cognitivos. Decidiu usar “principal” com base na experiência anterior armazenada em
seu LTM em vez de usar o nome real que você leu e que foi armazenado em seu
STM. Isso mostra que esses dois processos cognitivos não são tão separados um do outro.
outros como eu os apresentei.
Se o LTM é como o disco rígido do seu cérebro, armazenando memórias para sempre, você pode
pense no STM como a RAM do computador ou um cache que pode ser usado para
armazenar valores.

1.2.3 Memória de trabalho e programação

O terceiro processo cognitivo que desempenha um papel na programação é a memória de trabalho. STM
e LTM são principalmente dispositivos de armazenamento. Eles detêm informações, seja por um curto período de tempo
depois de ler ou ouvir, no caso de STM, ou por muito tempo, no caso de LTM.
Machine Translated by Google

Processos cognitivos em colaboração 9

O pensamento real, no entanto, não acontece no LTM ou STM, mas na memória de trabalho. É aqui
que novos pensamentos, ideias e soluções são formados. Se você pensar no LTM como um disco
rígido e no STM como RAM, a memória de trabalho é melhor comparada ao processador do cérebro.

PROGRAMA BÁSICO : MEMÓRIA DE TRABALHO

Ao ler o programa BASIC, você usa seu LTM—por exemplo, ao lembrar o significado de palavras-
chave como LET e EXIT. Além disso, você usa seu STM para armazenar algumas das informações
que encontra, como o fato de B$ começar como uma string vazia.

No entanto, seu cérebro faz muito mais enquanto você lê o programa BASIC. Você está
mentalmente tentando executar o código, para entender o que está acontecendo. Esse processo é
chamado de rastreamento – a compilação e execução mental do código. A parte do cérebro usada
para fazer o rastreamento e outras tarefas cognitivamente complexas é chamada de memória de trabalho.
Você pode compará-lo com o processador de um computador, que realiza cálculos.
Ao rastrear programas muito complexos, você pode sentir a necessidade de observar os valores
de variáveis, seja no código ou em uma tabela separada.
O fato de seu cérebro sentir a necessidade de armazenar informações externamente pode ser
um sinal de que sua memória de trabalho está muito cheia para processar mais informações.
Abordaremos essa sobrecarga de informações e como evitar que o cérebro se sobrecarregue no capítulo 4.

1.3 Processos cognitivos em colaboração


Na seção anterior, descrevi em detalhes três processos cognitivos importantes que são relevantes
para a programação. Em resumo, seu LTM armazena informações que você adquiriu por um longo
tempo, o STM armazena temporariamente informações que você acabou de ler ou ouvir e a memória
de trabalho processa informações e forma novos pensamentos.
Embora eu os tenha descrito como processos separados, esses processos cognitivos têm fortes
relações entre si. Vamos tocar em como eles se relacionam um com o outro.

1.3.1 Uma breve dissecação de como os processos cognitivos interagiram


Na verdade, todos os três processos cognitivos são ativados até certo ponto quando você pensa,
como ilustrado na figura 1.2. Você pode ter experimentado todos os três processos conscientemente
quando estava lendo o trecho de código Java anteriormente neste capítulo (lista 1.2). Algumas
informações foram armazenadas em seu STM, por exemplo, quando você lê que n é um número
inteiro. Ao mesmo tempo, seu cérebro recuperou o significado do que é um número inteiro de seu
LTM, e você estava pensando no significado do programa usando sua memória de trabalho.

Até agora neste capítulo, focamos especificamente nos processos cognitivos que acontecem
quando você lê código. No entanto, esses três processos cognitivos também estão envolvidos em
muitas outras tarefas relacionadas à programação.
Machine Translated by Google

10 CAPÍTULO 1 Decodificando sua confusão ao codificar

Longo prazo
Curto prazo
Filtro memória
memória

Em formação
1 2 3
4

Trabalhando
memória

Figura 1.2 Uma visão geral dos três processos cognitivos que este livro aborda: STM, LTM e
memória de trabalho. As setas rotuladas 1 representam informações que chegam ao seu cérebro.
As setas rotuladas 2 indicam as informações que prosseguem em seu STM. A seta 3 representa
as informações que viajam do STM para a memória de trabalho, onde são combinadas com as
informações do LTM (seta 4). A memória de trabalho é onde a informação é processada enquanto você pensa sobre ela.

1.3.2 Processos cognitivos relativos a tarefas de programação


Por exemplo, considere quando você lê um relatório de bug de um cliente. O bug parece
ser causado por um erro off-by-one. Este relatório de bug entra no cérebro através dos seus sentidos—
seus olhos se você for vidente, ou seus ouvidos se você ler com um leitor de tela. Para resolver o
bug, você deve reler o código que escreveu alguns meses atrás. Enquanto você está relendo
o código, seu STM armazena o que você lê, enquanto seu LTM informa sobre o que você implementou
há alguns meses - por exemplo, que você usou o modelo de ator na época. Além de memórias sobre
suas experiências, você também tem informações factuais armazenadas em
seu LTM, por exemplo, como você pode resolver um erro off-by-one. Todas essas informações - o novo
informações sobre o relatório de bug do seu STM e suas memórias pessoais e fatos relevantes sobre
como resolver bugs semelhantes do seu LTM — entram em sua memória de trabalho, onde você pode
pensar sobre o problema em questão.

EXERCÍCIO 1.1 Para praticar sua compreensão recém-adquirida dos três processos
cognitivos envolvidos na programação, preparei três programas.
Desta vez, porém, nenhuma explicação é dada sobre o que os trechos de código fazem. Você
terá, portanto, que ler os programas e decidir o que eles fazem por si mesmo. Os programas
são novamente escritos em APL, Java e BASIC, nessa ordem.
No entanto, cada um dos programas executa uma operação diferente, então você não pode
confie em sua compreensão do primeiro programa para apoiar a leitura do outro
programas.

Leia os programas com atenção e tente determinar o que eles fazem. Ao fazer
isso, reflita sobre os mecanismos que você usa. Use as perguntas da tabela a seguir para
orientar sua auto-análise.
Machine Translated by Google

Processos cognitivos em colaboração 11

Trecho de código 1 Trecho de código 2 Trecho de código 3

Você está recuperando conhecimento do LTM?

Se você estiver recuperando informações


do LTM, quais informações?

Você está armazenando informações em


seu STM?

Quais informações você está armazenando


explicitamente?

Que informação você está ignorando


porque parece irrelevante?

Sua memória de trabalho está processando


extensivamente algumas partes do código?

Quais partes do código colocam uma carga


pesada em sua memória de trabalho?

Você entende por que essas partes do


código fazem a memória de trabalho funcionar?

Trecho de código 1: um programa APL

f • {ÿÿ1:ÿ ÿ (ÿ ÿ-1)+ÿ ÿ-2}

O que faz este código? Que processos cognitivos estão envolvidos?

Trecho de código 2: um programa Java

classe pública Luhn {


public static void main(String[] args) {
System.out.println(luhnTest("49927398716"));
}

public static boolean luhnTest(String number){


int s1 = 0, s2 = 0;
String reverse = new StringBuffer(número).reverse().toString();
for(int i = 0 ;i < reverse.length();i++){
int digit = Character.digit(reverse.charAt(i), 10);
if(i % 2 == 0){//isto é para dígitos ímpares
s1 += dígito;
}else{//adicionar 2 * dígitos para 0-4, adicionar 2 * dígitos - 9 para 5-9
s2 += 2 * dígito;
if(dígito >= 5){
s2 -= 9;
}
}
}
retorno (s1 + s2) % 10 == 0;
}
}

O que faz este código? Que processos cognitivos estão envolvidos?


Machine Translated by Google

12 CAPÍTULO 1 Decodificando sua confusão ao codificar

Trecho de código 3: Um programa BASIC

100 INPUT PROMPT "String: ":TX$


120 DEIXE RES$=""
130 PARA I=LEN(TX$) PARA 1 PASSO-1
140 DEIXE RES$=RES$&TX$(I)
150 PRÓXIMO

160 IMPRIMIR RES$

O que faz este código? Que processos cognitivos estão envolvidos?

Resumo
ÿ A confusão durante a codificação pode ser causada por três problemas: falta de conhecimento,
falta de informações de fácil acesso ou falta de poder de processamento no cérebro.
ÿ Três processos cognitivos estão envolvidos quando você lê ou escreve código. ÿ
O primeiro processo é a recuperação da informação do LTM, onde o significado
de palavras-chave é armazenado, por
exemplo. ÿ No segundo processo, as informações sobre o programa em questão são armazenadas em seu
STM, que pode conter temporariamente informações como o nome de um método ou
variável.

ÿ O processo final envolvido é a memória de trabalho. É aqui que o processamento de


o código acontece, por exemplo, decidir que um índice é muito baixo.
ÿ Todos os três processos cognitivos estão funcionando enquanto você está lendo o código, e o
processos se complementam. Por exemplo, se seu STM encontrar um nome de variável como n,
seu cérebro pesquisará em seu LTM por programas relacionados que você
lido no passado. E quando você lê uma palavra ambígua, sua memória de trabalho
é ativado e seu cérebro tentará decidir o significado correto neste contexto.
Machine Translated by Google

Velocidade de leitura para código

Este capítulo abrange


ÿ Analisar por que ler código rapidamente é difícil, mesmo
para um desenvolvedor experiente
ÿ Dissecando como o cérebro divide novas
informações em partes reconhecíveis
ÿ Descobrir como LTM e STM trabalham juntos ao analisar
informações como palavras ou código
ÿ Examinando o papel da memória icônica ao processar
o código
ÿ Explicar como lembrar de código pode ser usado como
ferramenta para (auto)diagnóstico do nível de codificação
ÿ Praticar a escrita de código que é mais fácil para
os outros lerem

O Capítulo 1 introduziu três processos cognitivos que desempenham um papel na programação


e leitura de código. O primeiro processo cognitivo que abordamos foi o LTM, que você pode
pense como um disco rígido que armazena memórias e fatos. O segundo processo cognitivo
foi STM, que é como uma memória de acesso aleatório, armazenando informações que chegam
o cérebro por um curto período de tempo. Por fim, há a memória de trabalho, que funciona um pouco como
um processador e processa informações de LTM e STM para realizar o pensamento.

13
Machine Translated by Google

14 CAPÍTULO 2 Leitura rápida para código

O foco deste capítulo é a leitura de código. A leitura de código é uma parte maior da vida profissional de um
programador do que você imagina. Pesquisas indicam que quase 60% dos
o tempo dos programadores é gasto entendendo ao invés de escrever código .
a rapidez com que você pode ler o código, sem perder a precisão, pode ajudá-lo a melhorar seu
habilidades de programação substancialmente.
No capítulo anterior você aprendeu que STM é onde as informações são armazenadas primeiro
quando você lê o código. Este capítulo começará ajudando você a entender por que é tão
difícil processar muitas informações armazenadas em código. Se você sabe o que acontece no seu
cérebro quando você está lendo código rapidamente, você pode monitorar mais facilmente sua compreensão. Em
seguida, mostrarei métodos para melhorar suas habilidades de código, por exemplo, praticando a leitura rápida de vários
trechos de código. Ao final do capítulo, você saberá por que
ler o código é tão difícil. Você também entenderá como ler código mais rapidamente e
esteja ciente das técnicas que você pode usar para continuar melhorando suas habilidades de leitura de código.

2.1 Código de leitura rápida


O livro Estrutura e Interpretação de Programas de Computador por Harold Abelson, Gerald
Jay Sussman e Julie Sussman (MIT Press, 1996) contém esta frase bem conhecida:
“Os programas devem ser escritos para que as pessoas leiam e apenas incidentalmente para que as máquinas
executar." Isso pode ser verdade, mas a realidade é que os programadores praticam a escrita
codificam muito mais do que praticam a leitura de código.
Isso começa cedo. Ao aprender a programar, muitas vezes há muito foco na produção de código. Muito
provavelmente, quando você aprendeu a programar - seja na faculdade,
em um trabalho ou em um bootcamp - havia um forte foco na criação de código. Exercícios centrados em aprender
como resolver problemas e escrever código para eles. Exercícios onde você
código de leitura provavelmente não existiam. Devido a essa falta de prática, a leitura de código é
muitas vezes mais difícil do que precisa ser. Este capítulo o ajudará a melhorar suas habilidades de leitura de código.

A leitura do código é feita por vários motivos: para adicionar um recurso, para encontrar um bug ou para
construir uma compreensão de um sistema maior. O que todas as situações em que você está lendo código têm em
comum é que você está procurando informações específicas presentes em
o código. Exemplos de informações que você pode estar procurando são o local certo para implementar um novo
recurso, o local de um determinado bug, onde você editou pela última vez
o código ou como um determinado método é implementado.
Ao melhorar sua capacidade de encontrar rapidamente informações relevantes, você pode reduzir o
número de vezes que você tem que voltar ao código. Um nível mais alto de habilidade na leitura de código
também pode reduzir a frequência com que você precisa navegar pelo código para encontrar
em formação. O tempo que você economiza pesquisando pelo código pode ser gasto na correção
bugs ou adicionando novos recursos para que você possa se tornar um programador mais eficaz.
No capítulo anterior, pedi que você lesse programas em três linguagens de programação diferentes para ter uma
noção das três partes diferentes do cérebro em funcionamento. Mergulhar

1
Consulte “Medindo a compreensão do programa: um estudo de campo em larga escala com profissionais” de Xin Xia et al.
(2017), https://ieeexplore.ieee.org/abstract/document/7997917.
Machine Translated by Google

Código de leitura rápida 15

para o papel do STM com mais profundidade, veja o seguinte programa Java que implementa o
algoritmo de ordenação por inserção. Você pode olhar para o programa por não mais de três
minutos. Use um relógio ou cronômetro para saber quando o tempo acabou. Após os três minutos,
cubra o código Java com uma folha de papel ou com a mão.
Mantendo o código coberto, tente reproduzi-lo da melhor maneira possível.

Listagem 2.1 Um programa Java implementando a ordenação por inserção

public class InserçãoSort {


public static void main (String [] args) {
int [] matriz = {45,12,85,32,89,39,69,44,42,1,6,8}; temperatura interna; for (int i = 1; i
< array.length; i++) {

for (int j = i; j > 0; j--) {


if (array[j] < array [j - 1]) {
temp = array[j]; matriz[j] =
matriz[j - 1]; array[j - 1] = temp;

}
}

} for (int i = 0; i < array.length; i++) {


System.out.println(array[i]);
}
}
}

2.1.1 O que acabou de acontecer em seu cérebro?


Ao reproduzir o programa Java de classificação por inserção, você usou o STM e o LTM.
Isso é ilustrado na figura 2.1.

STM LTM
for (i = 1; i
Original int [ ] array int

temp para loop < array.


código
sobre array comprimento;
JAVA i++)

Figura 2.1 Uma ilustração dos processos


cognitivos em jogo ao lembrar o código.
Reproduzido Algumas partes do código são armazenadas
no STM, como nomes de variáveis e valores de
código variáveis. Para outras partes do código,
JAVA como a sintaxe de um loop for, você usa o
conhecimento armazenado em seu LTM.
Machine Translated by Google

16 CAPÍTULO 2 Leitura rápida para código

Seu STM conseguiu armazenar algumas das informações que você acabou de ler. Seu LTM adicionado a
esse conhecimento de duas maneiras. Primeiro, você pôde contar com o conhecimento sintático de Java.
Talvez você tenha se lembrado de “um loop for sobre o array”, que seu LTM sabe que é equivalente a
for (int i = 0; i < array.length; i++). Talvez você também tenha se lembrado de “imprimir tudo
elementos do array”, que em Java é para (i = 0; i < array.length; i++)
{System.out.println(array[i])}.
Segundo, você pode confiar no fato de saber que o código está implementando a classificação por
inserção. Isso pode ter ajudado você a preencher algumas lacunas no programa, que você
não conseguia lembrar com precisão. Por exemplo, talvez você não se lembre de ler o código que dois
valores do array foram trocados, mas porque você está familiarizado
com o tipo de inserção, você sabia que isso deveria acontecer em algum lugar.

2.1.2 Reexamine sua reprodução


Para mergulhar em seus processos cognitivos, dê uma segunda olhada no seu código reproduzido.
Anote quais partes do código você acha que vieram diretamente do seu STM e quais
as peças foram recuperadas do LTM. Um exemplo do código reproduzido com uma anotação de
processos cognitivos é mostrado na figura 2.2.

public class InsertionSort


InsertionSort { public class
public static void main (String [] args) {
array {45,12,…}
int [] matriz = {45,12,…};
temperatura interna;

for (int i = 1; i < array.length; i++) {


for (int j = i; j > 0; j--) {
if (array[j] < array [j - 1]) {
// troca j por j - 1 Figura 2.2 Um exemplo do código
temp = array[j]; da listagem 2.1 reproduzido por um
matriz[j] = matriz[j - 1]; programador Java experiente, anotado
array[j - 1] = temp;
para indicar o processo cognitivo em
}
jogo. As partes do código que são
}
sombreadas mais escuras são as partes
}
armazenadas no STM, e o sombreamento
// imprime matriz
; i++) for (int i = 0; i < array.length; i++) {
for (int i = 0; i < array.length; mais claro representa o código proveniente
System.out.println(array[i]);
do LTM. Observe que mais código é
} reproduzido aqui do que estava presente
} na listagem de código original - por
} exemplo, alguns comentários foram adicionados.

Quais informações são recuperadas do seu LTM, é claro, depende do que você tem
armazenado lá. Alguém menos experiente em Java provavelmente recuperará muito menos de seu
LTM, então a imagem pode parecer muito diferente para você. Observe também que nesta reprodução
estão presentes alguns comentários que não estavam no código Java original. Em experimentos que
realizei com programadores lembrando o código-fonte, descobri que
às vezes as pessoas adicionam comentários ao código reproduzido para facilitar a memorização. As
pessoas, por exemplo, primeiro escreveriam “imprimir a matriz” e depois preencheriam o
implementação. Você também fez isso?
Machine Translated by Google

Código de leitura rápida 17

Comentários são, é claro, frequentemente usados para descrever código que já está escrito, mas como
você pode ver neste exemplo, eles têm diversos usos e também podem ser usados como memória
ajuda para código futuro. Discutiremos o uso de comentários mais extensivamente mais adiante.
capítulos.

UMA SEGUNDA TENTATIVA DE LEMBRAR DE JAVA

No capítulo 1, expliquei como LTM e STM colaboram quando você está lendo código.
Você acabou de experimentar essa colaboração com mais profundidade ao reproduzir alguns
partes do programa de classificação por inserção Java a partir de informações armazenadas em seu LTM.
Para aprofundar sua compreensão de quão extensivamente você confia em seu LTM quando
lendo e entendendo o código, vamos fazer outro exercício. É o mesmo exercício que
anterior: inspecione o programa por no máximo três minutos, depois cubra
o código e tente reproduzi-lo da melhor maneira possível sem espiar!
O código Java na próxima listagem deve ser usado como um exercício de memória. Estude por três
minutos e tente reproduzir com sua melhor habilidade.

Listagem 2.2 Código Java público

void execute(int x[]){ int b = x.length;

for (int v = b / 2 - 1; v >= 0; v--) func(x, b, v);

// Extrai os elementos um por um


for (int l = b-1; l > 0; l--) {

// Move atual para terminar int temp


= x[0]; x[0] = x[l]; x[l] = temperatura;

func (x, l, 0);


}
}

2.1.3 Reexaminando sua segunda tentativa de reproduzir código


Sem saber nada sobre você ou sua experiência em Java, acho seguro apostar
que lembrar deste segundo programa foi provavelmente muito mais difícil. Existem algumas razões para isso.
Primeiro, você não sabe exatamente o que esse código está fazendo, o que o torna muito
mais difícil preencher as coisas que você esqueceu com o conhecimento do seu LTM.
Em segundo lugar, escolhi intencionalmente nomes de variáveis “estranhos”, como b e l iteradores de loop for.
Nomes desconhecidos tornam mais difícil para você detectar, reconhecer e
lembrar padrões. l como iterador de loop é especialmente enganoso, pois é visualmente tão semelhante a 1.
Machine Translated by Google

18 CAPÍTULO 2 Leitura rápida para código

2.1.4 Por que ler um código desconhecido é difícil?


Como o exemplo anterior mostrou, não é fácil reproduzir o código que você leu. Por que é
lembrando código tão difícil? A razão mais crucial é a capacidade limitada do seu
STM.

Você não pode armazenar fisicamente todas as informações que estão presentes no código que você está
lendo em seu STM para processá-lo. Como você aprendeu no capítulo 1, o STM armazena
informações que você lê ou ouve por um breve período de tempo. E quando digo breve, eu
realmente significa breve! Pesquisas indicam que ele não pode reter informações por mais de 30 segundos.
Após 30 segundos, você terá que armazenar as informações em seu LTM, ou
estar perdido para sempre. Imagine que alguém está lendo um número de telefone para você pelo telefone,
e você não tem um lugar para anotá-lo. Se você não encontrar um lugar para anotá-lo
em breve (por exemplo, um cache físico), você não se lembrará do número de telefone.
O tempo durante o qual você pode se lembrar de informações não é a única limitação de
STM—a segunda limitação é o tamanho.

Como em um computador, em seu cérebro o armazenamento de longo prazo é muito maior do que o
armazenamento de curto prazo. Mas enquanto para a RAM você pode estar falando de alguns gigabytes de
memória, o armazenamento rápido do seu cérebro é muito menor. Seu STM tem apenas alguns slots disponíveis
para informação. George Miller, um dos mais influentes pesquisadores da ciência cognitiva do século anterior,
descreveu esse fenômeno em seu artigo de 1956 “The Magical Number Seven, Plus or Minus Two: Some
Limits on Our Capacity for Processing
Em formação."

Pesquisas mais recentes indicam que o STM é ainda menor, estimando uma capacidade de
entre duas e seis coisas. Esse limite vale para quase todas as pessoas, e os cientistas até agora
não encontraram maneiras confiáveis de aumentar o tamanho do STM. Não é um milagre que
humanos podem fazer qualquer coisa com no máximo 1 byte de memória?
Para lidar com esses limites, seu STM colabora com seu LTM para entender
o que você está lendo ou lembrando. A próxima seção detalha como o STM colabora com o LTM para superar
suas limitações de tamanho.

2.2 Superando os limites de tamanho em sua memória


Na seção anterior você aprendeu sobre os limites do STM. Você os experimentou
em primeira mão, tentando lembrar um pedaço de código. No entanto, você provavelmente se lembrou
mais de seis caracteres do snippet de código. Isso não é uma contradição com ter
um máximo de apenas seis slots disponíveis para armazenar informações?
Nossa capacidade de armazenar apenas seis coisas no STM não se aplica apenas à leitura de código; isto
vale para qualquer tarefa cognitiva. Então, como é que as pessoas podem fazer qualquer coisa com uma
memória limitada? Como você pode ler esta frase, por exemplo?
De acordo com a teoria de Miller, depois de ler cerca de seis letras, você não deveria estar
começando a esquecer as primeiras letras que você lê? Claramente você pode lembrar e processar
mais de seis letras, mas como isso é possível? Para entender por que ler
código desconhecido é tão difícil, vamos ver um experimento importante que usou o xadrez para
nos ensine mais sobre STM.
Machine Translated by Google

Superando os limites de tamanho em sua memória 19

2.2.1 O poder da fragmentação


Os pedaços foram descritos pela primeira vez pelo matemático holandês Adrian de Groot. (De Groot,
a propósito, não é pronunciado como rimando com “boot” ou “tooth”, mas soa mais como
“crescimento.”) De Groot era um estudante de doutorado em matemática e um ávido jogador de xadrez. Ele
tornou-se profundamente interessado na questão de por que uma pessoa pode se tornar um grande xadrez
jogador, enquanto outros jogadores são obrigados a permanecer jogadores de xadrez “médios” por suas
vidas inteiras. Para investigar o tópico da habilidade no xadrez, de Groot realizou duas
experimentos.
No experimento 1, ilustrado na figura 2.3, de Groot mostrou uma configuração de xadrez para xadrez
jogadores por alguns segundos. Depois que os jogadores inspecionaram a configuração, as peças foram cobertas e os
jogadores tiveram que recriar a configuração de memória. O experimento de De Groot,
na verdade, foi semelhante ao exercício que você fez anteriormente neste capítulo com o código-fonte. De
Groot não estava interessado apenas na capacidade de todos de lembrar os locais do xadrez
peças. Ele comparou especificamente dois grupos de pessoas. O primeiro grupo era composto por
jogadores médios de xadrez, o segundo de jogadores experientes (mestres de xadrez). Ao comparar
o desempenho de jogadores médios com mestres de xadrez, de Groot descobriu que jogadores experientes eram muito
melhores em recriar as configurações de xadrez do que jogadores médios.

Especialista
Média Especialista Média
jogador jogador jogador jogador

Figura 2.3 O primeiro experimento de xadrez de De Groot no qual especialistas e jogadores de xadrez medianos foram
solicitados a se lembrar de uma configuração de xadrez. Jogadores experientes foram capazes de recordar mais peças do que jogadores médios.

A conclusão de De Groot desse experimento foi que os especialistas superaram a média


jogadores porque os jogadores de xadrez experientes simplesmente tinham um STM com uma capacidade maior do que
jogadores médios. Ele também levantou a hipótese de que um STM maior poderia ser a razão pela qual o
Machine Translated by Google

20 CAPÍTULO 2 Leitura rápida para código

jogadores experientes se tornaram especialistas em primeiro lugar; jogadores experientes podiam encaixar
mais peças de xadrez em suas memórias, o que lhes permitia jogar melhor também em partidas de xadrez.
No entanto, de Groot não estava totalmente convencido, então ele realizou outro experimento. Foi
semelhante ao primeiro experimento: novamente, jogadores médios e experientes foram solicitados a lembrar
as configurações de xadrez depois de inspecionar as configurações por um breve período de tempo.
A diferença estava nas próprias configurações de xadrez. Em vez de mostrar aos participantes uma configuração
de xadrez real como no experimento 1, ele mostrou a eles tabuleiros de xadrez com peças colocadas
aleatoriamente – e não apenas um pouco aleatórias; as peças de xadrez foram montadas em configurações
totalmente irreais. De Groot, então, comparou novamente o desempenho dos jogadores experientes com os
jogadores médios. Os resultados foram diferentes para o segundo experimento: ambos os tipos de jogadores
tiveram um desempenho igualmente ruim!
Os resultados do experimento 1 e do experimento 2 levaram de Groot a se aprofundar em como exatamente
os dois grupos de jogadores de xadrez se lembravam das configurações. Descobriu-se que, em ambos os
experimentos, os jogadores médios se lembraram principalmente das configurações, peça por peça.
Eles tentariam relembrar as configurações dizendo “uma torre em A7, um peão em B5, um rei em C8” e assim
por diante.

No primeiro experimento, no entanto, os jogadores de xadrez experientes exibiram uma estratégia


diferente. Os especialistas confiaram muito nas informações armazenadas em seu LTM. Por exemplo, eles
podem se lembrar de “uma abertura siciliana, mas um cavalo está duas casas à esquerda”. Lembrar da
configuração assim, claro, só é possível se você souber quais peças são usadas na abertura siciliana, que fica
armazenada no LTM. Relembrar as configurações como os especialistas fizeram ocupa apenas cerca de quatro
lugares na memória de trabalho (abertura siciliana, cavaleiro, 2, esquerda). Como você sabe, estima-se que o
STM contenha entre dois e seis elementos, então quatro elementos podem caber.

Alguns jogadores experientes também podem conectar as configurações ao seu próprio histórico de jogo
pessoal ou ao histórico de jogos que assistiram ou leram. Eles podem se lembrar de uma configuração como
“o jogo que joguei naquele sábado chuvoso de março contra Betsie, mas com roque à esquerda”. Essas
também são informações armazenadas no LTM. Lembrar uma configuração pensando em uma experiência
anterior também ocupa apenas alguns slots no STM.
Jogadores médios, no entanto, que estavam tentando se lembrar de todas as peças individuais, rapidamente
ficariam sem STM. Eles não podiam agrupar informações de maneira lógica como os especialistas faziam. Isso
explicava por que os jogadores medianos tiveram um desempenho pior do que os especialistas no experimento
1; quando o STM estava cheio, nenhum novo local podia ser armazenado.
De Groot chamou os grupos em que as pessoas combinavam pedaços de informação. Ele considerou a
“abertura siciliana”, por exemplo, um pedaço, que pode caber em um slot no STM. A teoria dos pedaços
também explica adequadamente por que ambos os tipos de jogadores tiveram desempenho igual no
experimento 2. Em uma configuração aleatória de xadrez, os jogadores experientes não podiam mais confiar
no repositório de tabuleiros em seu LTM para dividir rapidamente as peças em grupos maiores.

EXERCÍCIO 2.1 Talvez você esteja convencido pelos experimentos de de Groot, mas seria ainda
mais poderoso experimentar o chunking!
Machine Translated by Google

Superando os limites de tamanho em sua memória 21

Olhe para esta frase por cinco segundos e tente lembrá-la da melhor maneira possível
posso:

O quanto você consegue se lembrar dessa frase?

EXERCÍCIO 2.2 Agora tente se lembrar desta frase examinando-a por cinco segundos:

abk mrtpi gbar


Eu acho que a segunda frase foi mais fácil do que a primeira. Isso porque esta
segunda frase consiste em letras que você reconhece. Pode ser difícil de acreditar,
mas essas duas frases tinham o mesmo tamanho: 3 palavras, 12 caracteres, 9
caracteres diferentes.

EXERCÍCIO 2.3 Vamos tentar mais um. Esta frase novamente consiste em três palavras e
nove caracteres diferentes. Observe esta frase por cinco segundos e tente lembrá-la:

gato adora bolo

A terceira frase foi muito mais fácil do que as outras duas, certo? Você pode se
lembrar dessa frase com grande facilidade. Isso porque você pode dividir os
caracteres da terceira frase em palavras. Você pode então se lembrar de apenas três
pedaços: “gato”, “amores” e “bolo”. A quantidade de três elementos está bem abaixo
da capacidade do seu STM, então é possível lembrar toda essa frase facilmente,
enquanto nos dois primeiros exemplos o número de elementos provavelmente excede
os limites do seu STM.

CHUNKING EM CÓDIGO

Até agora neste capítulo, você viu que quanto mais informações você armazenou sobre um
tópico específico, mais fácil é dividir as informações em partes. Jogadores de xadrez experientes
têm muitas configurações de xadrez diferentes armazenadas em seu LTM, para que possam
se lembrar melhor de um tabuleiro. No exercício anterior, no qual você foi solicitado a lembrar
de caracteres, letras e palavras, você conseguiu lembrar as palavras com muito mais facilidade
do que caracteres desconhecidos. As palavras são mais fáceis de lembrar porque você pode
recuperar seu significado do seu LTM.
A descoberta de que é mais fácil lembrar quando você tem muito conhecimento sobre algo
em seu LTM também se aplica à programação. No restante deste capítulo, exploraremos os
resultados da pesquisa sobre programação e agrupamento especificamente. Depois disso,
vamos mergulhar em como praticar o código em pedaços e como escrever um código que seja
fácil de fragmentar.
Machine Translated by Google

22 CAPÍTULO 2 Leitura rápida para código

2.2.2 Programadores experientes podem lembrar de código melhor do que iniciantes


Os estudos de De Groot foram amplamente influentes na ciência cognitiva. Seus experimentos
também motivou pesquisadores de ciência da computação a estudar se resultados semelhantes
segure para programação.
Por exemplo, em 1981, Katherine McKeithen, pesquisadora do Bell Labs, tentou
repita os experimentos de Groot com programadores.2 Ela e seus colegas mostraram
pequenos programas ALGOL de 30 linhas para 53 pessoas: programadores iniciantes, intermediários e
experientes. Alguns dos programas eram programas reais, inspirados no primeiro
experimento no qual ele pedia aos participantes que se lembrassem de configurações realistas de xadrez. Dentro
Em outros programas, as linhas de código foram embaralhadas, semelhante ao segundo experimento de de
Groot, no qual as peças de xadrez foram colocadas em locais aleatórios. Os participantes foram
autorizados a estudar os programas ALGOL por dois minutos, após o que eles tiveram que reproduzir o código
da melhor maneira possível.
Os resultados de McKeithen foram bastante semelhantes aos de Groot: com os programas não
embaralhados, os especialistas se saíram melhor do que os programadores intermediários, e os programadores
intermediários, por sua vez, tiveram um desempenho melhor do que os iniciantes. Com os programas embaralhados
quase não havia diferença entre os três diferentes grupos de programadores,
como mostrado na figura 2.4.
A maior vantagem desta pesquisa é que os iniciantes serão capazes de processar muito
menos código do que especialistas. Isso pode ser importante para lembrar quando você está onboarding
um novo membro da equipe ou quando você está aprendendo uma nova linguagem de programação.

Programas regulares

20 Especialistas
18
16
14 Intermediários
12
10
8
6 Iniciantes
4
2

1 2 3 4 5
Número de programas

Programas embaralhados
Figura 2.4 O número de linhas de código que
20
18
iniciantes, intermediários e especialistas podem
16 lembrar nos experimentos de McKeithen et al. A
14
12 imagem superior mostra o desempenho dos
10
8
Especialistas
participantes em programas regulares, onde os
Intermediários
6 Iniciantes especialistas claramente se destacam. A imagem
4
2 inferior mostra o desempenho em programas
1 2 3 4 5 embaralhados e sem sentido; aqui, o desempenho
Número de programas de especialistas, intermediários e iniciantes é muito semelhante.

2
“Organização do Conhecimento e Diferenças de Habilidades em Programadores de Computador” por Katherine B. McKeithen et al.
(1981), http://mng.bz/YA5a.
Machine Translated by Google

Você vê mais código do que pode ler 23

Mesmo uma pessoa inteligente capaz de programar bem em uma linguagem ou contexto de programação diferente
terá dificuldades com palavras-chave, estruturas e conceitos de domínio desconhecidos quando ainda não estiverem
armazenados em seu LTM. Você aprenderá estratégias para fazer isso
de forma rápida e confiável no próximo capítulo.

2.3 Você vê mais código do que pode ler


Antes de nos aprofundarmos nos detalhes do STM, primeiro quero dedicar algum tempo ao que
acontece quando a informação entra no cérebro. Há uma fase que a informação passa
antes de atingir o STM, chamado de memória sensorial.
Em nossa metáfora do computador, você pode pensar na memória sensorial como um buffer de E/S
comunicar-se com um dispositivo de entrada como um mouse ou um teclado. Informações enviadas
de dispositivos periféricos é armazenado brevemente no buffer de E/S. Isso é verdade para o nosso sensorial
memória também; ele retém brevemente a entrada visual, auditiva ou tátil. Cada um dos cinco sentidos—
visão, audição, paladar, olfato, tato – tem seu próprio esconderijo na memória sensorial. Nem todos
estes são interessantes para discutir no contexto de programação, então neste capítulo nós
nos limitaremos à visão, chamada memória icônica.

2.3.1 Memória icônica


Antes que a informação chegue ao seu STM, ela primeiro entra na memória sensorial através
seus sentidos. Ao ler o código, a informação vem através de seus olhos, após o que
ele é armazenado brevemente na memória icônica.
A melhor maneira de ilustrar a memória icônica é imaginar que é véspera de Ano Novo,
e você está segurando um diamante. Se você mover o diamante rápido o suficiente, você pode formar
uma carta no ar. Você provavelmente nunca pensou sobre o que torna isso possível, mas
sua memória icônica é o que permite que você veja padrões na luz. A memória icônica pode
armazenar estímulos visuais, que são criados por uma imagem que você acabou de ver, por um tempo. Você pode
experimente outro exemplo se você fechar os olhos depois de ler esta frase. Com
seus olhos fechados, você ainda “vê” a forma da página. Isso também é memória icônica.
Antes de investigarmos como podemos usar a memória icônica na leitura de código, vamos examinar
e o que sabemos sobre isso. Um dos pioneiros da
pesquisa sobre memória icônica foi o psicólogo cognitivo americano
George Sperling, que pesquisou FCMB
memória nos anos 1960. Em seu estudo mais famoso, 3 participantes
viram uma grade de 3×3 ou 3×4 letras. Essa grade foi
semelhantes aos que você pode ver em um exame oftalmológico, mas com
QPVD
todas as letras sendo do mesmo tamanho, como mostra a figura 2.5.
Os participantes viram as imagens por um vigésimo
LXTA
de um segundo (0,05 segundos ou 50 milissegundos), após
Figura 2.5 Um exemplo da
que eles foram solicitados a recordar um selecionado aleatoriamente grade de letras de Sperling,
linha da grade, como a linha superior ou a coluna esquerda. que os participantes tiveram que lembrar

3
“A informação disponível em breves apresentações visuais” por George Sperling (1960), http://mng.bz/O1ao.
Machine Translated by Google

24 CAPÍTULO 2 Leitura rápida para código

Em 50 milissegundos eles nunca seriam capazes de realmente ler as letras, porque o tempo de reação de um olho
humano é de cerca de 200 milissegundos (um quinto de segundo) – o que é rápido, mas não rápido o suficiente. No
entanto, neste experimento, os participantes conseguiram se lembrar de todas as letras em uma linha ou coluna
aleatória da grade em cerca de 75% dos casos. Como os participantes estavam lembrando de uma linha aleatória,
o fato de terem conseguido 75% das vezes significava que haviam armazenado muitas ou todas as 9 ou 12 letras
na grade, o que é mais do que caberia no STM.

Não era o caso que esses participantes tivessem excelentes memórias. Quando os participantes foram
solicitados a reproduzir todas as letras, eles se saíram muito pior do que quando foram solicitados a nomear um
conjunto específico de letras. Normalmente, os participantes conseguiam se lembrar de cerca de metade das letras.
Essa segunda descoberta se encaixa com o que já sabemos sobre o STM, que também era conhecido quando
Sperling conduziu seus experimentos: ele pode armazenar até cerca de seis itens. O fato de muitos dos participantes
serem capazes de se lembrar de todas as letras quando solicitados apenas três ou quatro de cada vez, no entanto,
mostrou que a grade de letras em sua totalidade estava armazenada em algum lugar, e que “algum lugar” precisava
ser diferente de o STM com sua capacidade limitada. Sperling chamou a localização da informação proveniente do
sentido visual de memória icônica. Como mostrado em seus experimentos, nem todas as informações armazenadas
na memória icônica podem ser processadas no STM.

MEMÓRIA E CÓDIGO ICÔNICOS


Como você acabou de aprender, tudo o que você lê é armazenado primeiro em sua memória icônica. Mas nem tudo
que sua memória icônica armazena pode ser processado pelo seu STM; portanto, quando você lê o código em
detalhes, precisa fazer escolhas sobre o que pode processar. Essas escolhas, no entanto, não acontecem
conscientemente – ignorar certos detalhes no código geralmente acontece por acidente. Isso significa que você
pode, teoricamente, lembrar mais informações sobre o código do que pode processar em seu STM.

Você pode usar esse conhecimento para tentar ler o código com mais eficiência, primeiro olhando para ele por
um breve período de tempo e depois refletindo sobre o que viu. Este exercício de “código de relance” ajudará você
a obter uma imagem inicial do código.

EXERCÍCIO 2.4
Selecione um trecho de código que lhe seja familiar. Pode ser algo da sua própria base de código ou
um pequeno e simples código do GitHub. Não importa muito qual código você seleciona ou a
linguagem de programação usada. Algo do tamanho de meia página funciona melhor e, se possível,
é incentivada a impressão em papel.
Observe o código por alguns segundos, remova-o de vista e tente responder às seguintes perguntas:

ÿ Qual é a estrutura do código?

– O código está aninhado profundamente ou é plano?


– Existem linhas que se destacam? ÿ Como o
espaço em branco é usado para estruturar o código?
– Existem lacunas no código?
– Existem grandes bolhas de código?
Machine Translated by Google

Você vê mais código do que pode ler 25

2.3.2 Não é o que você lembra; é a maneira como você se lembra


Quando você tenta reproduzir o código que acabou de ler, estudando as linhas de código que você pode
reproduzir pode ser uma ótima ferramenta de diagnóstico que ajuda você a entender seu próprio
(mal entendido. No entanto, não é apenas o que você pode lembrar - a ordem em
que você lembra o código também pode ser uma ferramenta para a compreensão.
Os pesquisadores que repetiram os estudos de xadrez de Groot usando programas ALGOL
realizou outro experimento que forneceu mais insights sobre segmentação.4 No
segundo experimento, programadores iniciantes, intermediários e especialistas foram treinados para
lembre-se de 21 palavras-chave ALGOL, como IF, TRUE e END.
As palavras-chave utilizadas no estudo são apresentadas na figura 2.6. Se você quiser, você pode tentar
para se lembrar de todas as palavras-chave.

STRING CASE OU NULL ELSE STEP DO

PORQUE ENQUANTO A VERDADE É REAL ENTÃO DE Figura 2.6 As 21 palavras-


chave ALGOL que McKeithen et al.
usados em seu estudo. Iniciantes,
programadores intermediários e
especialistas foram treinados para
FALSE BITS LONGO E CURTO SE FIM aprender todas as 21 palavras-chave.

Quando os participantes conseguiram repetir todas as 21 palavras-chave de forma confiável, os pesquisadores pediram
para listar todas as palavras-chave. Se você também os memorizou, tente anotá-los
agora, para que você possa se comparar com as pessoas no estudo.
A partir da ordem em que os participantes repetiram as palavras-chave, McKeithen et al.
foram capazes de obter informações sobre as conexões que criaram entre as palavras-chave.
Os resultados do estudo mostraram que os iniciantes agruparam as palavras-chave ALGOL de maneiras diferentes dos
especialistas. Iniciantes, por exemplo, costumavam usar frases como memória
ajudas, como “VERDADEIRO É REAL E FALSO”. Os especialistas, no entanto, usariam seus conhecimentos prévios
conhecimento de programação para agrupar as palavras-chave; por exemplo, combinaram
VERDADEIRO e FALSO, e SE, ENTÃO e ENTÃO. Este estudo confirmou novamente que os especialistas
pensar sobre o código de uma maneira diferente do que os iniciantes fazem.

COMO ESCREVER O CÓDIGO CHUNKABLE

Depois de ter feito o exercício anterior de lembrar e fragmentar várias vezes, você
comece a desenvolver uma intuição sobre quais tipos de código são fragmentáveis. Do de Groot
estudo com enxadristas, sabemos que situações habituais ou previsíveis, como
aberturas famosas, facilidade de fragmentação. Então, se seu objetivo é criar tabuleiros de xadrez fáceis

4
Os resultados deste experimento foram relatados no mesmo artigo, “Knowledge Organization and Skill
Diferences in Computer Programmers” por Katherine B. McKeithen et al.
Machine Translated by Google

26 CAPÍTULO 2 Leitura rápida para código

para lembrar, use uma abertura bem conhecida. Mas o que podemos fazer no código para facilitar
ler? Vários pesquisadores estudaram maneiras de escrever código de tal forma que seja mais fácil de fragmentar e,
portanto, mais fácil de processar.

USAR PADRÕES DE DESIGN

Se você quiser escrever um código que seja fácil de fragmentar, use padrões de design. Aqueles
foram as descobertas de Walter Tichy, professor de ciência da computação na Karlsruhe
Instituto de Tecnologia da Alemanha. Tichy investigou o chunking no código, mas isso
aconteceu um pouco por acaso. Ele não estava estudando habilidades de memória de código, mas em vez disso
estava analisando padrões de design. Ele estava especialmente interessado na questão da
se os padrões de projeto ajudam os programadores quando eles estão mantendo o código (adicionando
novos recursos ou correção de bugs).
Tichy começou pequeno, com um grupo de alunos, testando se dar a eles informações sobre padrões de projeto
os ajudava a entender o código.5 Ele dividiu os alunos em
dois grupos: um grupo recebeu código com documentação, enquanto o outro grupo
recebeu o mesmo código, mas sem documentação sobre padrões de projeto. Os resultados do
O estudo de Tichy mostrou que ter um padrão de design presente no código era mais útil para realizar tarefas de
manutenção quando os programadores sabiam que o padrão
estava presente no código.
Tichy também realizou um estudo semelhante em profissionais.6 Nesse caso, os participantes
começou com o exercício de modificação de código, depois fez um curso sobre
Padrões de design. Após o curso, eles modificaram novamente o código, com ou sem design
padronizar. Os resultados deste estudo sobre os profissionais são apresentados na figura 2.7. Deveria ser
observou que os participantes deste estudo mantiveram código diferente após o teste, então
eles não estavam familiarizados com o código usado após o curso. O estudo usou duas bases de código: os
participantes que viram a base de código A antes do curso usaram a base de código B depois,
e vice versa.

Esta figura apresenta os resultados do estudo de Tichy com gráficos box-and - whisker.7
mostra que depois de fazer um curso sobre padrões de projeto (como visto nos gráficos à direita
rotulado como “pós-teste”), o tempo que os participantes precisaram para manter o código foi menor para o
código com padrões, mas não para o código sem padrões. Os resultados deste estudo indicam que adquirir
conhecimento sobre padrões de projeto, que provavelmente irá melhorar
sua capacidade de fragmentação, ajuda a processar o código mais rapidamente. Você também pode ver nos gráficos
que há uma diferença de efeito para diferentes padrões de projeto: a diminuição no tempo é
maior para o padrão observador do que para o padrão decorador.

5
Consulte "Dois experimentos controlados avaliando a utilidade das informações do padrão de projeto durante o programa
Manutenção”, de Lutz Prechelt, Barbara Unger e Walter Tichy (1998), http://mng.bz/YA9K.
6
“Um experimento controlado comparando a manutenibilidade de programas projetados com e sem design
Patterns—A Replication in a Real Programming Environment,” por Marek Vokácÿ, Walter Tichy, Dag IK
Sjøberg, Erik Arisholm, Magne Aldrin (2004), http://mng.bz/G6oR.
7
A caixa representa metade dos dados, sendo a linha superior a mediana do terceiro quartil e a linha inferior a
mediana do primeiro quartil. A linha dentro da caixa representa a mediana e os “bigodes” mostram os valores mínimo e
máximo.
Machine Translated by Google

Você vê mais código do que pode ler 27

Decorador
Pré-teste Pós-teste

Figura 2.7 Estes gráficos


mostram os resultados de Walter
80 Estudo de Tichy sobre
Tempo padrões de projeto com profissionais.
60
dentro

minutos
“Sem padrões” indica o tempo que
40
os participantes levaram para
20 modificar o código original sem
Sem Com Sem Com
padrões padrões padrões padrões padrões de projeto, enquanto “com
padrões” mostra o tempo que eles
levaram para o código que incluía

Observador padrões de projeto. “Pré-teste” é o


tempo que os participantes levaram
Pré-teste Pós-teste
antes do curso sobre padrões de
projeto; “pós-teste” é o tempo após
o curso de padrões de design. Os
80
resultados mostram que
Tempo
60
dentro

minutos após a manutenção do curso


40
o tempo foi significativamente
20 Sem Com Sem Com menor para código que contém
padrões padrões padrões padrões padrões de design.

ESCREVA COMENTÁRIOS

Você deve escrever comentários ou deve codificar “documentar-se”? Essa é uma pergunta que
muitas vezes provoca debates entre os programadores. Pesquisadores estudaram essa questão também
e encontrei várias direções interessantes para mergulhar.
A pesquisa mostrou que quando o código contém comentários, os programadores levam
mais tempo para lê-lo. Você pode pensar que isso é uma coisa ruim - que os comentários o atrasam
baixo - mas, na verdade, isso é um sinal de que os comentários estão sendo lidos quando os programadores
leia o código. No mínimo, isso está mostrando que você não está adicionando comentários ao seu
código para nada. Martha Elizabeth Crosby, pesquisadora da Universidade do Havaí,
estudou como os programadores leem o código e qual o papel dos comentários em sua leitura
comportamento.8 O trabalho de Crosby mostra que os iniciantes focam muito mais nos comentários do que
programadores experientes fazem. A Parte 4 deste livro abordará o processo de integração
colegas juniores em sua equipe com mais profundidade, mas as descobertas de Crosby sugerem que
adicionar comentários ao código pode ser uma boa maneira de facilitar para novos programadores
para entender sua base de código.
Além de apoiar programadores iniciantes, os comentários também desempenham um papel na forma como
código de bloco de desenvolvedores. Dissertação de Quiyin Fan de 2010 na Universidade de Maryland,
“Os Efeitos de Beacons, Comentários e Tarefas no Processo de Compreensão do Programa
em Manutenção de Software”, mostrou que os desenvolvedores dependem muito de comentários

8
“Como lemos algoritmos? Um estudo de caso” por Martha E. Crosby e Jan Stelovsky, https://ieeexplore.ieee
.org/document/48797.
Machine Translated by Google

28 CAPÍTULO 2 Leitura rápida para código

ao ler o código. Em particular, comentários de alto nível como “Esta função imprime um
dada árvore binária em ordem” pode ajudar os programadores a fragmentar pedaços maiores de código.
Comentários de baixo nível, por outro lado, como “Incrementar i (por um)” após uma linha que
lê i++;, pode criar um fardo no processo de fragmentação.

DEIXE SINALIZADORES

Uma última coisa que você pode fazer para facilitar o processo de fragmentação de código é incluir bea cons.
Beacons são partes de um programa que ajudam um programador a entender o que o
código faz. Você pode pensar em um beacon como uma linha de código, ou mesmo parte de uma linha de código,
que seu olho cai, o que faz você pensar: “Aha, agora eu vejo”.
Beacons normalmente indicam que um pedaço de código contém certas estruturas de dados,
algoritmos ou abordagens. Como exemplo de beacons, considere o seguinte Python
código que percorre uma árvore binária.

Listagem 2.3 Travessia de árvore em ordem em Python

# Uma classe que representa um nó em uma árvore

classe Nó:
def __init__(self, key): self.left = Nenhum

self.right = Nenhum
self.val = chave

# Uma função para percorrer a árvore em ordem

def print_in_order(root): se root:

# Primeira repetição no filho esquerdo


print_in_order(root.left)

# então imprima os dados do nó print(root.val)

# agora se repete no filho direito


print_in_order(root.right)

print("O conteúdo da árvore é")


print_in_order(árvore)

Este código Python contém vários beacons dos quais um leitor pode deduzir que este
O código usa uma árvore binária como estrutura de dados:

ÿ Comentários usando a palavra “árvore”


ÿ Variáveis chamadas raiz e árvore

ÿ Campos chamados esquerda e direita

ÿ Conteúdo da string no código que diz respeito às árvores (“O conteúdo da árvore é”)
Machine Translated by Google

Você vê mais código do que pode ler 29

Os beacons fornecem um importante serviço de sinalização para os programadores durante o processo de


compreensão porque muitas vezes agem como um gatilho para os programadores confirmarem ou
refutar hipóteses sobre a fonte. Por exemplo, quando você começa a ler o Python
código na listagem anterior, você pode não ter ideia do que esse código está fazendo.
Quando você lê o primeiro comentário e a classe Node, você tem a sensação de que este código
diz respeito às árvores. Os campos à esquerda e à direita restringem ainda mais sua hipótese, indicando que esse
código funciona em uma árvore binária.
Normalmente distinguimos dois tipos diferentes de sinalizadores: sinalizadores simples e sinalizadores compostos.
balizas.

Beacons simples são elementos de código sintático autoexplicativos, como


nomes de variáveis. No código Python anterior, root e tree são beacons simples. Dentro
algum código, operadores como +, > e && e instruções estruturais como if, else,
e assim por diante também podem ser considerados beacons simples porque são simples o suficiente para
processo e pode ajudar um leitor a desbloquear a funcionalidade do código por conta própria.
Beacons compostos são estruturas de código maiores compostas de beacons simples. Beacons compostos
fornecem significado semântico para funções que beacons simples executam
juntos. No código Python na listagem 2.3, self.left e self.right formam juntos
um farol composto. Separadamente, eles não oferecem muitas informações sobre o código, mas
juntos eles fazem. Elementos de código também podem servir como sinalizadores compostos. Por exemplo, um
for-loop pode ser um beacon composto porque contém a variável, a atribuição inicial e o incremento e os valores de
limite.
Os beacons podem assumir muitas formas; já vimos que os nomes de variáveis e classes
podem servir como beacons, e outros identificadores, como nomes de métodos, também podem ser beacons.

Além de nomes, construções de programação específicas como uma troca ou inicialização para
uma lista vazia também pode ser beacons.
Os beacons têm uma relação com os pedaços, mas a maioria dos pesquisadores os vê como conceitos diferentes.
Beacons são geralmente vistos como partes menores do código do que pedaços. Crosby,
cujo trabalho sobre o uso de comentários eu resumi anteriormente, também estudou o papel dos bea cons. Ela

descobriu que programadores experientes, mas não novatos, fazem uso intenso de bea cons quando estão lendo e
compreendendo código.9 O exercício a seguir irá
ajudá-lo a reconhecer beacons úteis.

EXERCÍCIO 2.5
Selecionar os tipos certos de beacons para usar no código pode exigir alguma prática. Usa isto
exercício para praticar deliberadamente o uso de beacons no código.

Etapa 1: selecione o código


Para este exercício, selecione uma base de código desconhecida, mas selecione uma em uma linguagem de
programação com a qual você esteja familiarizado. Se possível, seria ótimo fazer este exercício em um
codebase onde você conhece alguém familiarizado com os detalhes. Você pode então usar essa pessoa como um
juiz de sua compreensão. Na base de código, selecione um método ou função.

9
Consulte “Os papéis que os sinalizadores desempenham na compreensão para programadores iniciantes e experientes” de Martha E. Crosby,
Jean Scholtz e Susan Wiedenbeck, http://mng.bz/zGKX.
Machine Translated by Google

30 CAPÍTULO 2 Leitura rápida para código

Passo 2: Estude o
código Estude o código selecionado e tente resumir o significado do código.
Etapa 3: observe ativamente os beacons que
você usa Sempre que tiver um momento “aha” em que você se aproxima um pouco da funcionalidade de
o código, pare e anote o que o levou a essa conclusão. Isso poderia
ser um comentário, um nome de variável, um nome de método ou um valor intermediário - todos
aqueles podem ser balizas.

Passo 4: Reflita
Quando você tiver uma compreensão completa do código e uma lista de beacons, reflita
usando estas perguntas:

ÿ Que balizas você coletou?


ÿ Esses são elementos de código ou informações de linguagem natural?
ÿ Que conhecimento eles representam?
ÿ Representam conhecimento sobre o domínio do código?
ÿ Eles representam conhecimento sobre a funcionalidade do código?

Etapa 5: contribuir de volta para o código (opcional)


Às vezes, mas nem sempre, os beacons que você selecionou podem ser melhorados ou
estendido. Ou o código pode precisar de beacons adicionais que ainda não existem.
Este é um ótimo momento para enriquecer o código com os beacons novos ou aprimorados. Porque
você não estava familiarizado com o código antes deste exercício, você tem uma boa perspectiva
sobre o que ajudaria alguém que é novo na base de código também.
Etapa 6: comparar com outra pessoa (opcional)
Se você tem um colega de trabalho ou amigo que também quer melhorar o uso do beacon, você pode
fazer este exercício juntos. Pode ser interessante refletir sobre as diferenças de vocês dois
tinha na reprodução de código. Como sabemos que existem grandes diferenças entre iniciantes e
especialistas, este exercício também pode ajudá-lo a entender seu nível de habilidade em um
linguagem de programação em relação a outra.

2.3.3 Pratique o agrupamento


Os estudos descritos anteriormente neste capítulo mostraram que pessoas com mais experiência
podem se lembrar de mais peças de xadrez, palavras ou linhas de código. Ao saber mais
conceitos de programação vem com a experiência, há várias coisas que você pode fazer para
pratique a segmentação de código deliberadamente.
Em muitos lugares deste livro, você verá a expressão prática deliberada. Deliberar
prática é usar pequenos exercícios para melhorar uma certa habilidade. As flexões são deliberadas
prática para os músculos do braço; as escadas de tom são uma prática deliberada para músicos;
soletrar palavras é uma prática deliberada para novos leitores. Na programação, por muitas razões
diferentes, a prática deliberada não é tão comumente usada. Muitas pessoas têm
aprendi programação principalmente programando muito. Enquanto isso funciona, pode não
trabalhar o mais eficazmente possível. Praticar deliberadamente o agrupamento, tentando ativamente
lembre-se de código é um ótimo exercício.
Machine Translated by Google

Você vê mais código do que pode ler 31

EXERCÍCIO 2.6
Este exercício ajuda você a reconhecer com quais conceitos você está familiarizado e quais conceitos são mais difíceis
para você, testando sua memória de leitura de código. O subjacente
suposição é que, como mostrado pelos experimentos descritos, conceitos familiares são mais fáceis
lembrar. O que você pode lembrar é o que você sabe, então esses exercícios podem ser
usado para (auto)diagnóstico do seu conhecimento de código.

Etapa 1: selecione o código


Selecione uma base de código com a qual você esteja familiarizado—talvez algo com o qual você trabalhe
regularmente, mas não principalmente. Também pode ser algo que você escreveu pessoalmente há algum tempo.
Certifique-se de ter pelo menos algum conhecimento da linguagem de programação que o código
está escrito. Você tem que saber mais ou menos o que o código faz, mas não o conhece intimamente. Você quer estar

em uma situação semelhante à dos jogadores de xadrez; eles conhecem a placa


e as peças, mas não a configuração.
Na base de código, selecione um método ou função, ou outro pedaço de código coerente
aproximadamente o tamanho de meia página, com um máximo de 50 linhas de código.

Passo 2: Estude o
código Estude um pouco o código selecionado, por no máximo dois minutos. Defina um temporizador para que você
não perca a noção do tempo. Depois que o cronômetro acabar, feche ou cubra o código.

Etapa 3: Reproduza o código Pegue


um pedaço de papel ou abra um novo arquivo em seu IDE e tente recriar o código como
melhor que puder.

Passo 4: Reflita
Quando tiver certeza de ter reproduzido todo o código possível, abra o código original e compare. Reflita usando estas
perguntas:

ÿ Quais peças você produziu corretamente com facilidade?


ÿ Existem partes do código que você reproduziu parcialmente?
ÿ Existem partes do código que você perdeu completamente?
ÿ Você entende por que você perdeu as falas que você fez?
ÿ As linhas de código que você perdeu contêm conceitos de programação que são
desconhecido para você?
ÿ As linhas de código que você perdeu contêm conceitos de domínio que não são familiares
ia para você?

Etapa 5: comparar com outra pessoa (opcional)


Se você tem um colega de trabalho que também quer melhorar suas habilidades de segmentação, você pode fazer
este exercício juntos. Pode ser muito interessante refletir sobre as diferenças no
código que você reproduz. Porque sabemos que existem grandes diferenças entre iniciantes
e especialistas, este exercício também pode ajudá-lo a entender seu nível de habilidade em uma linguagem de
programação em relação ao de outra pessoa.
Machine Translated by Google

32 CAPÍTULO 2 Leitura rápida para código

Resumo ÿ O
STM tem capacidade de dois a seis elementos. ÿ Para
superar a limitação de tamanho, seu STM colabora com seu LTM quando
você se lembra das informações.
ÿ Quando você lê uma nova informação, seu cérebro tenta dividir a informação em partes
reconhecíveis chamadas pedaços. ÿ Quando você não tem conhecimento suficiente em
seu LTM, precisa confiar na leitura de código de baixo nível, como letras e palavras-chave. Ao
fazer isso, você rapidamente ficará sem espaço em seu STM.

ÿ Quando seu LTM armazena informações relevantes suficientes, você pode se lembrar de
conceitos abstratos como “um loop for em Java” ou “classificação de seleção em Python”
em vez do código em um nível inferior, ocupando menos espaço em seu STM. ÿ Quando
você lê o código, ele é armazenado primeiro na memória icônica. Apenas um bit do código é
enviado posteriormente para o STM. ÿ Lembrar o código pode ser usado como uma
ferramenta para (auto)diagnóstico do seu conhecimento de codificação. Como você pode
lembrar mais facilmente o que já sabe, as partes do código que você lembra podem revelar
os padrões de design, construções de programação e conceitos de domínio com os quais
você está mais familiarizado. ÿ O código pode conter características que facilitam o
processamento, como design
padrões, comentários e sinalizadores explícitos.
Machine Translated by Google

Como aprender
programação
sintaxe rapidamente

Este capítulo abrange


ÿ Examinar por que o amplo conhecimento de sintaxe é importante
ÿ Selecionando técnicas para lembrar a sintaxe de programação
ÿ Organizar o que você pode fazer para evitar o esquecimento da sintaxe
ÿ Deduzir quando estudar sintaxe e conceitos de programação
para os resultados mais eficazes

ÿ Descobrir como os conceitos de sintaxe e programação são


armazenado em LTM

ÿ Praticar a elaboração para fortalecer memórias e


lembrar conceitos de programação

Este capítulo se concentra em como as pessoas aprendem a se lembrar das coisas. Este capítulo irá
ajudá-lo a entender por que certos conhecimentos permanecem, enquanto outros são esquecidos. Por
exemplo, em um ponto você provavelmente aprendeu que System.out.print()

33
Machine Translated by Google

34 CAPÍTULO 3 Como aprender a sintaxe de programação rapidamente

é o método que imprime em Java. No entanto, você não tem todos os métodos do Java
memorizados. Tenho certeza de que você às vezes sentiu a necessidade de procurar uma
sintaxe específica. Por exemplo, você saberia se deve usar addDays(), addTimespan() ou
plusDays() para adicionar um dia a um DateTime?
Você pode não se importar muito em saber a sintaxe de cor – afinal, todos nós podemos
procurar informações online, certo? No entanto, como o capítulo anterior mostrou, o que você
já sabe influencia a eficiência com que você processa o código. Portanto, memorizar a sintaxe
de programação, os conceitos e as estruturas de dados o ajudarão a processar o código
mais rápido.

Este capítulo apresenta quatro técnicas importantes que o ajudarão a memorizar melhor
e mais facilmente os conceitos de programação. Isso fortalecerá seu armazenamento a longo
prazo de conceitos de programação, o que, por sua vez, permitirá melhor fragmentação e
leitura de código. Se você já teve dificuldade em lembrar a sintaxe do Flexbox em CSS, a
ordem dos parâmetros no método boxplot() do matplotlib ou a sintaxe de funções anônimas
em JavaScript, este capítulo o cobriu!

3.1 Dicas para lembrar a sintaxe


Nos capítulos anteriores, você viu que tentar lembrar o código linha por linha é difícil.
Lembrar qual sintaxe usar ao produzir código também pode ser um desafio. Por exemplo,
você pode escrever código da memória para as seguintes situações? ÿ Lendo um arquivo
hello.txt e gravando todas as linhas na linha de comando ÿ Formatando uma data na
ordem dia-mês-ano ÿ Uma expressão regular combinando palavras que começam com
“s” ou “temporada”

Mesmo que você seja um programador profissional, talvez precise pesquisar algumas das
sintaxes específicas para esses exercícios. Neste capítulo, exploraremos não apenas por que
é difícil lembrar a sintaxe correta, mas também como aprimorá-la. Primeiro, porém,
examinaremos mais profundamente por que é tão importante que você possa se lembrar do código.
Muitos programadores acreditam que se você não conhece um determinado trecho de
sintaxe, você pode simplesmente procurá-lo na internet e que, portanto, o conhecimento de
sintaxe não é tão importante. Há duas razões pelas quais “só olhando as coisas” pode não
ser uma ótima solução. A primeira razão foi abordada no capítulo 2: o que você já sabe afeta
em grande medida a eficiência com que você pode ler e entender o código. Quanto mais
conceitos, estruturas de dados e sintaxe você conhece, mais código você pode facilmente
fragmentar e, assim, lembrar e processar.

3.1.1 As interrupções prejudicam seu fluxo de trabalho


A segunda razão é que uma interrupção do seu trabalho pode ser mais perturbadora do que
você pensa. Apenas abrir um navegador para pesquisar informações pode tentá-lo a verificar
seu e-mail ou ler algumas notícias, que podem não ser relevantes para a tarefa em questão.
Você também pode se perder lendo discussões detalhadas em sites de programação quando
estiver procurando informações relacionadas.
Machine Translated by Google

Como aprender a sintaxe rapidamente com flashcards 35

Chris Parnin, professor da North Carolina State University, estudou extensivamente o que acontece quando
os programadores são interrompidos no trabalho.1 Parnin registrou
10.000 sessões de programação por 85 programadores. Ele observou com que frequência os desenvolvedores
foram interrompidos por e-mails e colegas (o que foi muito!), mas ele também examinou
o que acontece depois de uma interrupção. Parnin determinou que as interrupções são, sem surpresa, bastante
perturbadoras para a produtividade. O estudo mostrou que normalmente leva
cerca de um quarto de hora para voltar a editar o código após uma interrupção. Quando
interrompido durante a edição de um método, os programadores puderam retomar seu trabalho
em menos de um minuto em apenas 10% dos casos.
Os resultados de Parnin mostraram que os programadores muitas vezes esqueciam informações vitais sobre o
código em que estavam trabalhando enquanto estavam longe do código. Você pode reconhecer
aquele “O que eu estava fazendo de novo?” sentimento depois de retornar ao código de uma pesquisa. Os
programadores do estudo de Parnin muitas vezes também precisavam fazer um esforço deliberado para reconstruir
o contexto. Por exemplo, eles podem navegar para vários locais na base de código para recuperar
detalhes antes de continuar o trabalho de programação.
Agora que você sabe por que lembrar de sintaxe é importante, vamos mergulhar em como
aprenda a sintaxe rapidamente.

3.2 Como aprender a sintaxe rapidamente com flashcards


Uma ótima maneira de aprender qualquer coisa rapidamente, incluindo sintaxe, é usar flashcards. Flashcards
são simplesmente cartões de papel ou Post-Its. Um lado tem um prompt nele - a coisa que você
quero aprender. O outro lado tem o conhecimento correspondente sobre ele.
Ao usar flashcards para programação, você pode escrever o conceito de um lado
e o código correspondente no outro. Um conjunto de flashcards para compreensão de lista
em Python pode ficar assim:

1 Compreensão básica <-> números = [x para x em números]


2 Compreensão com filtro <-> odd_numbers = [x para x em números se x % 2 == 1]
3 Compreensão com cálculo <-> [x*x para x em números]
4 Compreensão com filtro e cálculo <-> quadrados = [x*x para x em números
se x > 25]

Você usa flashcards lendo o lado do cartão com o prompt e tentando


seu melhor para lembrar a sintaxe correspondente. Escreva a sintaxe em uma parte separada
de papel ou digite o código em um editor. Quando terminar, vire o cartão e
compare o código para ver se você acertou.
Flashcards são comumente usados para aprender uma segunda língua e são extremamente úteis para isso.
No entanto, aprender francês usando flashcards pode ser uma tarefa complicada porque há muitas palavras.
Mesmo as grandes linguagens de programação como
C++ são muito menores do que qualquer linguagem humana natural. Aprendendo um bom pedaço de
os elementos sintáticos básicos de uma linguagem de programação, portanto, são possíveis com um esforço
relativamente baixo.

1
Consulte “Estratégias de retomada para tarefas de programação interrompidas” por Chris Parnin e Spencer Rugaber
(2011), http://mng.bz/0rpl.
Machine Translated by Google

36 CAPÍTULO 3 Como aprender a sintaxe de programação rapidamente

EXERCÍCIO 3.1 Pense nos 10 principais conceitos de programação que você sempre teve
dificuldade para lembrar.

Faça um conjunto de flashcards para cada um dos conceitos e tente usá-los. Você pode
também faça isso de forma colaborativa em um grupo ou equipe, onde você pode descobrir que
você não é o único que luta com certos conceitos.

3.2.1 Quando usar os flashcards


O truque para aprender a sintaxe é usar os flashcards frequentemente para praticar. Existem, no entanto,
muitos aplicativos, como Cerego, Anki e Quizlet, que permitem que você crie seus
próprios flashcards digitais. O benefício desses aplicativos é que eles lembram quando você deve praticar
novamente. Se você usar os flashcards de papel ou um aplicativo regularmente, verá que depois de um
algumas semanas seu vocabulário sintático terá aumentado significativamente. Isso vai te salvar
muito tempo no Google, limitar as distrações e ajudá-lo a segmentar melhor.

3.2.2 Expandindo o conjunto de flashcards


Há bons momentos para adicionar flashcards ao seu conjunto. Primeiro, quando você está aprendendo um novo
linguagem de programação, estrutura ou biblioteca, você pode adicionar um cartão sempre que
encontrar um novo conceito. Por exemplo, se você acabou de começar a aprender a sintaxe de list
compreensões, crie o cartão ou cartões correspondentes imediatamente.
Um segundo grande momento para expandir seu conjunto de cartões é quando você está prestes a pesquisar no Google um

determinado conceito. Isso é um sinal de que você ainda não conhece esse conceito de cor. Escrever
o conceito que você decidiu procurar em um lado de um cartão e o código que você encontrou
o outro lado.

Claro, você precisará usar seu julgamento. Linguagens de programação modernas,


bibliotecas e APIs são enormes e não há necessidade de memorizar toda a sua sintaxe.
Procurar coisas online é totalmente bom para elementos ou conceitos sintáticos marginais.

3.2.3 Afinando o conjunto de flashcards


Quando você usa seus flashcards regularmente, depois de um tempo você pode começar a sentir que
conheça bem algumas das cartas. Quando isso acontecer, você pode querer diminuir o seu conjunto
de cartas um pouco. Para acompanhar o quão bem você conhece certos conceitos, você pode manter um
pequeno registro em cada cartão de suas respostas certas e erradas, conforme mostrado na Figura 3.1.

Contagem

Frente Costas

Todos os elementos da lista Figura 3.1 Exemplo de um flashcard com


números = [x para x
uma contagem de respostas certas e
usando um em números]
erradas. Usando uma contagem, você
compreensão da lista pode acompanhar qual conhecimento já
está armazenado de forma confiável em seu LTM.
Machine Translated by Google

Como não esquecer as coisas 37

Se você acertar uma carta várias vezes seguidas, você pode descartá-la. Claro, se você se encontrar
com dificuldades novamente, você sempre pode colocar o cartão de volta. Se você usar um aplicativo
para fazer flashcards, os aplicativos normalmente diminuirão o conjunto para você, não mostrando
cartões que você já conhece bem.

3.3 Como não esquecer as coisas A


seção anterior mostrou como você pode usar flashcards para aprender rapidamente e lembrar
facilmente a sintaxe. Mas quanto tempo você deve praticar? Quando você finalmente
conhecerá a totalidade do Java 8? Esta seção esclarecerá com que frequência você terá que
revisitar o conhecimento antes que sua memória seja perfeita.
Antes de podermos voltar nossa atenção para como não esquecer as coisas, precisamos
mergulhar em como e por que as pessoas esquecem. Você já sabe que o STM tem limitações - ele
não pode armazenar muitas informações de uma só vez e as informações que armazena não são
retidas por muito tempo. O LTM também tem limitações, mas são diferentes.
O grande problema com o seu LTM é que você não consegue se lembrar das coisas por muito
tempo sem prática extra. Depois de ler, ouvir ou ver algo, as informações são transferidas do seu
STM para o seu LTM, mas isso não significa que elas ficarão armazenadas no LTM para sempre.
Nesse sentido, o LTM de um ser humano difere significativamente do disco rígido de um computador,
no qual as informações são armazenadas com relativa segurança e durabilidade.
O decaimento do LTM não é de segundos, como o do STM, mas ainda é muito mais curto do que
você imagina. A curva de esquecimento é ilustrada na figura 3.2. Como você pode ver, em uma hora
você normalmente já terá perdido metade do que leu. Depois de dois dias, apenas 25% do que você
aprendeu permanece. Mas esse não é o quadro completo – o gráfico na Figura 3.2 representa o
quanto você se lembra se não revisitar as informações.

lembrou

70

60

50

40

30

20

10
Figura 3.2 Um gráfico que ilustra quanta
informação você lembra depois de ser exposto
a ela. Após dois dias, apenas 25% do
10 20 30 40 50 horas conhecimento permanece em seu LTM.
Machine Translated by Google

38 CAPÍTULO 3 Como aprender a sintaxe de programação rapidamente

3.3.1 Por que esquecemos as memórias?


Para entender por que algumas memórias são esquecidas tão rapidamente, precisamos mergulhar fundo
no funcionamento do LTM – começando com como as coisas são lembradas.
O armazenamento de memórias no cérebro não é baseado em zeros e uns, mas compartilha um nome
com a forma como armazenamos informações em disco: codificação. Quando eles falam sobre codificação,
no entanto, os cientistas cognitivos não estão se referindo precisamente à tradução de pensamentos para
armazenamento – o funcionamento exato desse processo ainda é amplamente desconhecido – mas sim
às mudanças que acontecem no cérebro quando as memórias são formadas por neurônios. .

HIERARQUIA VERSUS REDES

Comparamos o LTM a um disco rígido, mas o armazenamento no cérebro na verdade não funciona
hierarquicamente, da mesma forma que os arquivos são organizados em pastas com subpastas. Como
mostra a Figura 3.3, as memórias no cérebro são organizadas mais em uma estrutura de rede.
Isso porque os fatos estão ligados a um grande número de outros fatos. A consciência dessas conexões
entre diferentes fatos e memórias é importante para entender por que as pessoas esquecem de informações,
que é o tema que vamos discutir a seguir.

lar Pinguim
Coisas Águia
Pomba
pássaros.txt Jardim zoológico

casas.txt Cabana

Pardal

Figura 3.3 Duas formas de organização de dados: à esquerda, um sistema de arquivos hierárquico;
à direita, memórias organizadas em rede

A CURVA DO ESQUECIMENTO

Hermann Ebbinghaus foi um filósofo e psicólogo alemão que se interessou em estudar as capacidades da
mente humana na década de 1870. Naquela época, a ideia de medir as capacidades mentais das pessoas
era relativamente desconhecida.
Ebbinghaus queria entender os limites da memória humana usando sua própria memória como teste.
Ele queria empurrar-se o mais longe possível. Ele percebeu que tentar lembrar palavras ou conceitos
conhecidos não era um teste verdadeiro porque as memórias são armazenadas com associações ou
relacionamentos entre si. Por exemplo, se você tentar lembrar a sintaxe de uma compreensão de lista,
saber a sintaxe de um loop for pode ajudá-lo.

Para criar um meio mais justo de avaliação, Ebbinghaus construiu um grande conjunto de palavras
curtas e sem sentido, como wix, maf, kel e jos. Ele então conduziu um extenso conjunto de experimentos,
usando a si mesmo como cobaia. Durante anos, ele estudou listas dessas palavras sem sentido lendo-as
em voz alta, com a ajuda de um metrônomo para acompanhar o ritmo, e registrou quanta prática era
necessária para ser capaz de lembrar cada lista perfeitamente.
Machine Translated by Google

Como não esquecer as coisas 39

Em 1880, depois de uma década, ele estimou que havia passado quase 1.000 horas praticando e podia
recitar 150 por minuto. Ao testar-se em diferentes intervalos, Ebbing haus foi capaz de estimar o intervalo de
tempo de sua memória. Ele resumiu suas descobertas em
sua publicação de 1885 Über das Gedächtnis (Memória: Uma Contribuição para a Psicologia Experimental).
Seu livro continha a fórmula para o esquecimento mostrada na figura 3.4, que se encontra em
a base do conceito da curva do esquecimento.

1,84
b = 100 x
Figura 3.4 Fórmula de Ebbinghaus
(log10t) 1,25 + 1,84 para estimar quanto tempo uma memória durará

Um estudo recente do professor holandês Jaap Murre, da Universidade de Amsterdã, confirmou que a fórmula
de Ebbinghaus está amplamente correta.2

3.3.2 Repetição espaçada


Sabemos agora com que rapidez as pessoas esquecem as coisas, mas como isso nos ajudará a não esquecer
a sintaxe de boxplot () em matplotlib ou compreensões de lista em Python? Acontece que
que os experimentos de Ebbinghaus em lembrar palavras sem sentido não só lhe permitiram
prever quanto tempo ele levaria para esquecer as coisas, mas também lançar luz sobre como
evitar o esquecimento. Ebbinghaus observou que ele poderia aprender de forma confiável um conjunto de 12
palavras sem significado com 68 repetições no dia 1, seguidas de 7 no dia seguinte (tempos totais: 75),
ou ele poderia estudar o conjunto de palavras apenas 38 vezes espaçadas ao longo de 3 dias,
que é metade do tempo de estudo.
Extensas pesquisas foram feitas sobre a curva do esquecimento, desde pesquisas sobre procedimentos
matemáticos simples, como adição com crianças pequenas, até fatos biológicos para altas
escolares. Um estudo que lançou mais luz sobre a distância ideal entre repetições foi conduzido por Harry
Bahrick, da Ohio Wesleyan University, que novamente usou
ele mesmo como cobaia, mas também incluiu sua esposa e dois filhos adultos, que foram
também cientistas e interessados no tema.3
Todos eles estabeleceram a meta de aprender 300 palavras estrangeiras; sua esposa e filhas estudavam
palavras francesas, e o próprio Bahrick estudava alemão. Eles dividiram o
palavras em 6 grupos de 50 e utilizou espaçamentos diferentes para as repetições de cada
grupo. Cada grupo de palavras foi estudado 13 ou 26 vezes em um intervalo definido de 2, 4,
ou 8 semanas. A retenção foi então testada após 1, 2, 3 ou 5 anos.
Um ano após o término do período de estudo, Bahrick e sua família descobriram que
lembraram mais palavras do grupo de 50 que mais estudaram e
com os intervalos mais longos entre as práticas—26 sessões, cada 8 semanas de intervalo. Elas
foram capazes de lembrar 76% das palavras desse grupo um ano depois, contra 56% no

2
Veja “Replicação e Análise da Curva de Esquecimento de Ebbinghaus” de Jaap Murre (2015), https://journals.plos
.org/plosone/article?id=10.1371/journal.pone.0120644.
3
Ver “Manutenção do Vocabulário de Língua Estrangeira e o Efeito de Espaçamento” por Harry Bahrick et al. (1993),
www.gwern.net/docs/spacedrepetition/1993-bahrick.pdf.
Machine Translated by Google

40 CAPÍTULO 3 Como aprender a sintaxe de programação rapidamente

grupo estudado em um intervalo de duas semanas. A evocação diminuiu nos anos seguintes,
mas permaneceu consistentemente maior para o mesmo grupo de palavras que eles estudaram
por mais tempo.
Em resumo, você se lembra por mais tempo se estudar por um período mais longo. Isso
não significa que você precise passar mais tempo estudando; significa que você deve estudar
em intervalos mais espaçados. Revisitar seu conjunto de flashcards uma vez por mês será
suficiente para ajudar sua memória a longo prazo, e também é relativamente factível! Isso, é
claro, contrasta fortemente com a educação formal, onde tentamos acumular todo o
conhecimento em um semestre, ou com bootcamps que buscam educar as pessoas em três
meses. O conhecimento aprendido dessa maneira só é válido se você continuar repetindo o
que aprendeu com frequência depois.

DICA A maior lição desta seção é que a melhor maneira que a ciência conhece
para evitar o esquecimento é praticar regularmente. Cada repetição fortalece sua
memória. Após várias repetições espaçadas por um longo período, o conhecimento
deve permanecer em seu LTM para sempre. Se você já se perguntou por que
esqueceu muito do que aprendeu na faculdade, é por isso! A menos que você
revisite o conhecimento, ou seja forçado a pensar sobre ele, você perderá memórias.

3.4 Como lembrar a sintaxe por mais tempo


Agora você sabe que praticar a sintaxe é importante porque ajuda você a fragmentar o código
e economizará muito tempo na pesquisa. Também abordamos com que frequência você deve
praticar; não tente memorizar todos os seus flashcards em um dia, mas espalhe seu estudo
por um período mais longo. O restante do capítulo falará sobre como praticar. Abordaremos
duas técnicas para fortalecer memórias: prática de recuperação (tentar ativamente lembrar de
algo) e elaboração (conectar ativamente novos conhecimentos a memórias existentes).

Você deve ter notado antes que eu não lhe disse para simplesmente ler os dois lados dos
flashcards. Em vez disso, pedi que você lesse o lado que contém o prompt, que é o que faz
você lembrar da sintaxe.
Isso porque a pesquisa mostrou que tentar ativamente lembrar torna as memórias mais
fortes. Mesmo que você não saiba a resposta completa, as memórias são mais fáceis de
encontrar quando você já tentou encontrá-las antes. O restante deste capítulo explorará essa
descoberta com mais profundidade e ajudará você a aplicá-la ao aprendizado de programação.

3.4.1 Duas formas de lembrar informações


Antes de mergulharmos em como a lembrança pode fortalecer as memórias, primeiro
precisamos entender o problema com mais profundidade. Você pode pensar que uma memória
está armazenada em seu cérebro ou não, mas a realidade é um pouco mais complexa. Robert
e Eliza beth Bjork, professores de psicologia da Universidade da Califórnia, distinguiram dois
mecanismos diferentes na recuperação de informações do LTM: força de armazenamento e
força de recuperação.
Machine Translated by Google

Como lembrar a sintaxe por mais tempo 41

FORÇA DE ARMAZENAMENTO

A força de armazenamento indica quão bem algo está armazenado no LTM. Quanto mais você estuda
algo, mais forte é a memória dele, até que se torna praticamente impossível esquecê-lo. Você pode
imaginar esquecer que 4 vezes 3 é 12? No entanto, nem todas as informações que você armazenou em
seu cérebro são tão fáceis de acessar quanto as tabelas de multiplicação.

FORÇA DE RECUPERAÇÃO

A força de recuperação indica quão fácil é lembrar de algo. Tenho certeza de que você já teve aquela
sensação de ter certeza de que sabe alguma coisa (um nome, uma música, um número de telefone, a
sintaxe da função filter() em JavaScript), mas não consegue se lembrar; a resposta está na ponta da
língua; você simplesmente não pode alcançá-lo. Essa é uma informação para a qual a capacidade de
armazenamento é alta — quando você finalmente se lembra dela, não consegue acreditar que não
conseguiu lembrar dela —, mas a capacidade de recuperação é baixa.
É geralmente aceito que a força de armazenamento só pode aumentar – pesquisas recentes indicam
que as pessoas nunca esquecem realmente as memórias4 – e que é a força de recuperação das memórias
que decai com o passar dos anos. Quando você estuda repetidamente alguma informação, você fortalece
a capacidade de armazenamento desse fato. Quando você tenta se lembrar de um fato que você sabe
que sabe, sem estudo extra, você melhora a capacidade de força de recuperação.

3.4.2 Apenas ver a informação não é suficiente


Quando você está procurando por uma sintaxe específica, o problema geralmente está na força da
recuperação, não na força do armazenamento. Por exemplo, você pode encontrar o código correto para
percorrer uma lista em C++ ao contrário das opções na listagem a seguir?

Listagem 3.1 Seis opções para percorrer uma lista em C++

1. rit = s.rbegin(); rit != s.rend(); rit++ 2. rit = s.revbegin(); rit !=


s.end(); rit++ 3. rit = s.beginr(); rit != s.endr(); rit++ 4. rit = s.beginr();
rit != s.end(); rit++ 5. rit = s.rbegin(); rit != s.end(); rit++ 6. rit =
s.revbeginr(); rit != s.revend(); rit++

Todas essas opções parecem bastante semelhantes, e mesmo para um programador C++ experiente
pode ser complicado lembrar a versão correta, apesar de ter visto esse código várias vezes. Quando lhe
disserem a resposta certa, parecerá que você sabia o tempo todo: “Claro que é rit = s.rbegin(); rit !=
s.rend(); rit++!”
Portanto, o problema aqui não é a força com que o conhecimento é armazenado em seu LTM (a força
de armazenamento), mas a facilidade com que você pode encontrá-lo (a força de recuperação). Este
exemplo mostra que mesmo que você já tenha visto o código uma dúzia de vezes antes, simplesmente
ser exposto ao código pode não ser suficiente para que você consiga se lembrar dele. As informações são
armazenadas em algum lugar em seu LTM, mas não estão prontamente disponíveis quando você precisa delas.

4
“Recordação, Familiaridade e Reintegração Cortical: Uma Análise de Padrão Multivoxel”, de Jeffrey D. Johnson,
Susan GR McDuff, Michael D. Rugg e Kenneth A. Norman. Neurônio, v. 63, não. 5, 8 de setembro de 2009.
Machine Translated by Google

42 CAPÍTULO 3 Como aprender a sintaxe de programação rapidamente

3.4.3 Relembrar informações fortalece as memórias


O exercício da seção anterior mostrou que não basta simplesmente armazenar informações em
seu LTM. Você também precisa ser capaz de recuperá-lo com facilidade. Como muitas coisas na
vida, recuperar informações fica mais fácil quando você pratica muito. Como você nunca tentou
realmente lembrar a sintaxe, é difícil lembrá-la quando precisar. Sabemos que tentar ativamente
lembrar de algo fortalece a memória – essa é uma técnica que remonta a Aristóteles.

Um dos primeiros estudos sobre a prática de recuperação foi feito por um professor chamado
Philip Boswood Ballard, que publicou um artigo sobre o tema chamado “Obliviscence and
Reminiscence” em 1913. Ballard treinou um grupo de alunos para memorizar 16 versos do poema
“The Wreck do Hesperus”, que conta a história de um capitão vaidoso que causa a morte de sua
filha. Quando ele examinou seu desempenho de recordação, ele observou algo interessante.
Sem dizer aos alunos que eles seriam testados novamente, Ballard os fez reproduzir o poema
novamente após dois dias. Como não sabiam que outro teste estava por vir, os alunos não
estudaram o poema mais adiante. Durante o segundo teste, os alunos lembraram em média 10%
a mais do poema. Depois de mais dois dias, suas pontuações de recordação foram mais altas
novamente. Suspeitando do resultado, Ballard repetiu esses estudos várias vezes - mas
encontrou resultados semelhantes a cada vez: quando você tenta ativamente se lembrar de
informações sem estudo adicional, vai se lembrar mais do que aprendeu.

Agora que você está familiarizado com a curva de esquecimento e os efeitos da prática de
recuperação, deve ficar ainda mais claro por que simplesmente procurar uma sintaxe que você
não conhece toda vez que precisar não é uma boa ideia. Como pesquisar é tão fácil e é uma
tarefa tão comum, nossos cérebros sentem que não precisam realmente lembrar a sintaxe.
Portanto, a força de recuperação da sintaxe de programação permanece fraca.
O fato de não nos lembrarmos da sintaxe, é claro, leva a um ciclo vicioso.
Como não nos lembramos, precisamos procurá-lo. Mas, como continuamos pesquisando em vez
de tentar lembrá-lo, nunca melhoramos a força de recuperação desses conceitos de programação
- portanto, devemos continuar pesquisando-os, ad infinitum.
Então, da próxima vez que você estiver prestes a pesquisar algo no Google, pode valer a
pena primeiro tentar ativamente lembrar a sintaxe. Mesmo que não funcione dessa vez, o ato de
tentar lembrar pode fortalecer sua memória e pode ajudá-lo a lembrar da próxima vez.
Se isso não funcionar, faça um flashcard e pratique ativamente.

3.4.4 Fortaleça as memórias pensando ativamente


Na seção anterior, você aprendeu que tentar ativamente lembrar de informações, com prática de
recuperação, faz com que a informação fique melhor na sua memória. Você também sabe que
praticar a memorização de informações por um longo período funciona melhor. Mas há uma
segunda maneira de fortalecer as memórias: pensando ativamente e refletindo sobre as
informações. O processo de pensar sobre as informações que você acabou de aprender é
chamado de elaboração. A elaboração funciona especialmente bem para aprender conceitos de
programação complexos.
Machine Translated by Google

Como lembrar a sintaxe por mais tempo 43

Antes de nos aprofundarmos no processo de elaboração e como você pode usá-lo para melhor
aprender novos conceitos de programação, precisamos examinar mais de perto como o armazenamento no
cérebro funciona.

ESQUEMA
Você viu que as memórias em seu cérebro são armazenadas em uma forma de rede, com relações com
outras memórias e fatos. As maneiras pelas quais os pensamentos e as relações entre eles são organizados
em nossas mentes são chamados de esquemas ou esquemas.
Quando você aprende novas informações, você tentará encaixar as informações em um esquema
em seu cérebro antes de armazená-lo em LTM. Informações que se ajustam melhor a um esquema existente
será mais fácil de lembrar. Por exemplo, se eu pedir para você se lembrar dos números 5, 12,
91, 54, 102 e 87 e depois liste três deles para um belo prêmio de sua escolha, que
ser difícil de fazer, porque não há “ganchos” para conectar as informações. Esta lista
será armazenado em um novo esquema: “Coisas que estou lembrando para ganhar um belo prêmio”.
No entanto, se eu pedir para você se lembrar de 1, 3, 15, 127, 63 e 31, isso pode ser mais fácil de
Faz. Se você pensar um pouco, verá que esses números se encaixam em uma categoria de números:
números que, quando transformados em uma representação binária, consistem em apenas uns. Você não só
consegue lembrar esses números com mais facilidade, mas também
também pode estar mais motivado a lembrar os números porque esta é uma
coisa para fazer. Você sabe que se você conhece o limite superior dos números em bits, isso pode
ajudar a resolver alguns problemas.
Lembre-se que quando a memória de trabalho processa informações, ela também procura
o LTM para fatos e memórias relacionados. Quando suas memórias estão conectadas a cada
outro, encontrar as memórias é mais fácil. Em outras palavras, a força de recuperação é maior para
memórias que se relacionam com outras memórias.

Quando salvamos memórias, as memórias podem até ser alteradas para se adaptarem
aos esquemas existentes. Na década de 1930, o psicólogo britânico Frederic Bartlett conduziu uma
experimento em que ele pediu às pessoas que estudassem um conto de nativos americanos chamado The
Guerra dos Fantasmas e tente relembrar a história semanas ou até meses depois .
suas descrições, Bartlett pôde ver que os participantes haviam mudado a história para
suas crenças e conhecimentos existentes. Por exemplo, alguns participantes omitiram detalhes
consideravam irrelevantes. Outros participantes tornaram a história mais “ocidental”
e de acordo com suas normas culturais; por exemplo, substituindo um arco por uma arma. Isto
experimento mostrou que as pessoas não se lembram de palavras e fatos simples, mas que
adaptar memórias para se adequarem às suas memórias e crenças existentes.
O fato de as memórias poderem ser alteradas assim que as armazenamos pode ter

lados. Duas pessoas envolvidas na mesma situação podem se lembrar dela de maneira totalmente diferente
depois, porque seus próprios pensamentos e ideias afetarão a forma como elas armazenam os dados.
recordações. Mas também podemos usar o fato de que as memórias podem ser alteradas ou salvas em nosso
vantagem ao armazenar informações conhecidas relevantes junto com as informações adicionadas.

5
Veja seu livro Remembering: A Study in Experimental and Social Psychology (Cambridge University Press, 1932).
Machine Translated by Google

44 CAPÍTULO 3 Como aprender a sintaxe de programação rapidamente

USANDO A ELABORAÇÃO PARA APRENDER NOVOS CONCEITOS DE PROGRAMAÇÃO

Como você viu anteriormente neste capítulo, as memórias podem ser esquecidas quando a força de
recuperação - a facilidade com que você pode coletar informações - não é forte o suficiente.
O experimento de Bartlett mostra que mesmo no primeiro salvamento, quando uma memória é inicialmente
armazenada no LTM, alguns detalhes da memória podem ser alterados ou esquecidos.
Por exemplo, se eu lhe disser que James Monroe foi o quinto presidente dos Estados Unidos, você
pode se lembrar que Monroe é um ex-presidente, mas esquecer que ele era o quinto antes de guardar a
memória. O fato de você não se lembrar do número pode ser causado por vários fatores; por exemplo,
você pode pensar que é irrelevante, ou muito complexo, ou pode ter se distraído. Existem muitos fatores
que afetam o quanto é armazenado, incluindo seu estado emocional. Por exemplo, é mais provável que
você se lembre de um determinado bug que o manteve em sua mesa durante a noite há um ano do que
um bug aleatório que você corrigiu em alguns minutos hoje.

Embora você não possa mudar seu estado emocional, há muitas coisas que você pode fazer para
salvar o máximo possível de uma nova memória. Uma coisa que você pode fazer para fortalecer a
codificação inicial das memórias é chamada de elaboração. Elaboração significa pensar sobre o que você
quer lembrar, relacionando-o com memórias existentes e fazendo com que as novas memórias se
encaixem em esquemas já armazenados em seu LTM.
A elaboração pode ter sido uma das razões pelas quais os alunos do estudo de Ballard foram mais
capazes de lembrar as palavras do poema ao longo do tempo. Recordar repetidamente o poema forçava
os alunos a preencher as palavras que faltavam, cada vez as memorizando. Eles também provavelmente
conectaram partes do poema a outras coisas de que se lembravam.
Se você quiser se lembrar melhor das novas informações, é útil elaborar explicitamente as informações.
O ato de elaboração fortalece a rede de memórias relacionadas, e quando uma nova memória tem mais
conexões é mais fácil recuperá-la. Imagine que você está aprendendo um novo conceito de programação,
como compreensão de listas em Python. Uma compreensão de lista é uma maneira de criar uma lista com
base em uma lista existente. Por exemplo, se você quiser criar uma lista dos quadrados de números que
você já armazenou em uma lista chamada números, você pode fazer isso com esta compreensão de lista:

quadrados = [x*x para x em números]

Imagine que você está aprendendo este conceito pela primeira vez. Se você quiser se ajudar a lembrá-lo
melhor, ajuda muito elaborar deliberadamente pensando em conceitos relacionados. Por exemplo, você
pode tentar pensar em conceitos relacionados em outras linguagens de programação, em conceitos
alternativos em Python ou em outras linguagens de programação, ou em como esse conceito se relaciona
com outros paradigmas.

EXERCÍCIO 3.2 Use este exercício na próxima vez que aprender um novo conceito de
programação. Responder às seguintes perguntas o ajudará a elaborar e fortalecer a nova
memória:
Machine Translated by Google

Resumo 45

ÿ Que conceitos esse novo conceito faz você pensar? Anote todos os
conceitos relacionados.
ÿ Então, para cada um dos conceitos relacionados que você pode pensar, responda a estas perguntas:
– Por que o novo conceito me faz pensar nesse conceito que já
conhecer?

– Compartilha sintaxe?
– É usado em um contexto semelhante?

– Este novo conceito é uma alternativa a um que já conheço?


ÿ Que outras maneiras você conhece de escrever código para atingir o mesmo objetivo? Tente criar o
maior número possível de variantes desse snippet de código.
ÿ Outras linguagens de programação também possuem esse conceito? Você pode escrever
exemplos de outras linguagens que suportam operações semelhantes? Como eles diferem do
conceito em questão?
ÿ Este conceito se encaixa em um determinado paradigma, domínio, biblioteca ou framework?

Resumo
ÿ É importante saber um pouco de sintaxe de cor porque mais conhecimento de sintaxe facilitará o
agrupamento. Além disso, pesquisar a sintaxe pode interromper seu trabalho.
ÿ Você pode usar flashcards para praticar e lembrar a nova sintaxe, com um prompt
um lado e código do outro lado.

ÿ É importante praticar novas informações regularmente para combater a deterioração da memória.


ÿ O melhor tipo de prática é a prática de recuperação, onde você tenta se lembrar de informações
informação antes de procurá-lo.
ÿ Para maximizar a quantidade de conhecimento que você lembra, espalhe sua prática
hora extra.

ÿ As informações em seu LTM são armazenadas como uma rede conectada de fatos relacionados.
ÿ A elaboração ativa de novas informações ajuda a fortalecer a rede de memórias à qual a nova memória
se conectará, facilitando a recuperação.
Machine Translated by Google

Como ler
código complexo

Este capítulo abrange:


ÿ Analisando o que acontece quando sua memória de
trabalho é sobrecarregada por código complexo
ÿ Comparando dois tipos diferentes de memória de trabalho
sobrecarga ao programar
ÿ Refatoração de código para legibilidade para compensar
uma memória de trabalho sobrecarregada
ÿ Criando uma tabela de estado e um gráfico de dependência
para suportar sua memória de trabalho ao ler código
complexo

O Capítulo 1 apresentou as diferentes maneiras pelas quais o código pode ser confuso. Nós temos
visto que a confusão pode ser causada pela falta de informação, que deve ser
adquiridos e armazenados em seu STM, ou por falta de conhecimento, que exige o armazenamento
informações em seu LTM. Este capítulo cobre a terceira fonte de confusão: a falta de
do poder de processamento no cérebro.
Às vezes, o código que você está lendo é muito complexo para você entender completamente
Compreendo. Porque ler código não é uma atividade que a maioria dos programadores pratica

46
Machine Translated by Google

Por que é difícil entender um código complexo 47

muitas vezes, você pode achar que não tem estratégias para lidar com a leitura de código que você não conhece.
Compreendo. Técnicas comuns como “ler de novo” e “desistir” não são úteis.
Nos capítulos anteriores, abordamos técnicas para ajudá-lo a ler melhor o código. Dentro
no capítulo 2, você aprendeu sobre técnicas para fragmentação de código mais eficaz e
capítulo 3 forneceu dicas para armazenar mais conhecimento de sintaxe em seu LTM, que também
auxilia na leitura do código. No entanto, às vezes o código é tão complexo que mesmo com muito
conhecimento de sintaxe e estratégias eficientes de agrupamento ainda é muito difícil de processar.
Este capítulo mergulha nos processos cognitivos que fundamentam o poder de processamento
do cérebro, que comumente chamamos de memória de trabalho. Vamos explorar o que é memória de trabalho e como
o código pode ser tão confuso que sobrecarrega sua memória de trabalho. Depois de abordarmos o básico, mostrarei
três técnicas para apoiar sua
memória de trabalho para que você possa processar códigos complexos com mais facilidade.

4.1 Por que é difícil entender um código complexo


No capítulo 1, mostrei a você um exemplo de programa BASIC cuja execução foi complicada o suficiente para que
você provavelmente não pudesse processar tudo apenas lendo o código. Dentro
Nesse caso, você pode ficar tentado a rabiscar valores intermediários ao lado do código, como
mostrado na figura 4.1.

1 LET N2 = ABS (INT (N))


2 LET B$ =
""
7
3 PARA N1 = N2 PARA 0 PASSO 0
4 LET N2 = INT (N1 / 2) 3
5

6
LET B$ = STR$ (N1 - N2 * 2) + B$
DEIXE N1 = N2
"1"
7 PRÓXIMO N1
8 IMPRIMIR B$
9 DEVOLUÇÃO

Figura 4.1 Um programa convertendo o número N em uma representação binária


em BASIC. O programa é confuso porque você não consegue ver todos os
pequenos passos que estão sendo executados. Se você precisa entender todos
os passos, você pode usar um auxílio de memória como anotar os valores intermediários das variáveis.

O fato de você sentir a necessidade de fazer isso significa que seu cérebro não tem a capacidade de
processar o código. Vamos comparar o código BASIC com o segundo exemplo do capítulo
1, um programa Java que calcula a representação binária de um inteiro n. Enquanto
interpretar esse código também pode exigir alguma energia mental, e é possível que a falta de familiaridade com o
funcionamento interno do método toBinaryString() possa causar
alguma confusão, é improvável que você sinta a necessidade de fazer anotações durante a leitura.

Listagem 4.1 Um programa Java para converter n em uma representação binária

public class BinaryCalculator { public static void


main(Integer n) {
System.out.println(Integer.toBinaryString(n));
}
}
Machine Translated by Google

48 CAPÍTULO 4 Como ler código complexo

Não saber sobre o funcionamento interno de toBinaryString() pode causar confusão. Dentro
capítulos anteriores, investigamos dois dos processos cognitivos em jogo quando você lê
código complexo: STM e LTM. Para entender por que às vezes você precisa descarregar
informação, você precisa entender o terceiro processo cognitivo apresentado no capítulo 1, que ainda não
discutimos em detalhes. A memória de trabalho representa a
capacidade do cérebro de pensar, formar novas ideias e resolver problemas. Anteriormente, comparamos o STM
com a RAM de um computador e o LTM com o disco rígido. Segue
nessa analogia, a memória de trabalho é como o processador do cérebro.

4.1.1 Qual é a diferença entre memória de trabalho e STM?


Algumas pessoas usam memória de trabalho como sinônimo de STM, e você já deve ter visto
os dois termos usados alternadamente. Outros, no entanto, distinguem entre os dois
conceitos, e faremos isso neste livro. O papel do STM é lembrar as informações. O papel da memória de
trabalho, por outro lado, é processar informações. Nós
tratará esses processos como separados.

DEFINIÇÃO A definição de memória de trabalho que usaremos no restante


deste livro é “STM aplicado a um problema”.

A Figura 4.2 mostra um exemplo da diferença entre os dois processos: se você estiver
lembrando de um número de telefone, você usa seu STM, enquanto que, se estiver adicionando números inteiros
você usa sua memória de trabalho.

542
121
542-121-851 851

Figura 4.2 O STM armazena informações


brevemente (como um número de
telefone, conforme mostrado à esquerda),
Curto prazo enquanto a memória de trabalho
Trabalhando processa as informações (como ao
memória memória realizar um cálculo, conforme mostrado à direita).

Como você viu no capítulo 2, o STM normalmente só pode conter de dois a seis itens por vez.
Mais informações podem ser processadas quando as informações são divididas em pedaços reconhecíveis,
como palavras, aberturas de xadrez ou padrões de design. Porque o trabalho
memória é o STM aplicado a um determinado problema, tem a mesma limitação.
Como o STM, a memória de trabalho só é capaz de processar de duas a seis coisas
de uma vez. No contexto da memória de trabalho, essa capacidade é conhecida como a capacidade cognitiva .
carga. Quando você está tentando resolver um problema que envolve muitos elementos que
não pode ser dividido eficientemente em pedaços, sua memória de trabalho se tornará
"sobrecarregado."
Machine Translated by Google

Por que é difícil entender um código complexo 49

4.1.2 Tipos de carga cognitiva no que se refere à programação


Este capítulo apresentará métodos para abordar sistematicamente a carga cognitiva, mas
antes de podermos cobrir essas técnicas, precisamos explorar os diferentes tipos que
existir. O pesquisador que primeiro propôs a teoria da carga cognitiva foi o professor australiano John
Sweller. Sweller distinguiu três tipos diferentes de carga cognitiva:
intrínsecos, estranhos e pertinentes. A Tabela 4.1 fornece um resumo rápido de como a carga cognitiva
varia.

Tabela 4.1 Tipos de carga cognitiva

Tipo de carga Breves explicações

Carga intrínseca Quão complexo é o problema em si

Carga estranha O que as distrações externas adicionam ao problema

carga alemã Carga cognitiva criada por ter que armazenar seu pensamento no LTM

Vamos nos concentrar nos dois primeiros tipos de carga cognitiva aqui; discutimos a carga alemã em
mais profundidade em um capítulo posterior.

CARGA COGNITIVA INTRÍNSECA AO LER O CÓDIGO


A carga cognitiva intrínseca é a carga cognitiva causada por recursos
de um problema que o problema contém por natureza. Por
Por exemplo, imagine que você tenha que calcular a hipotese de um
triângulo, conforme ilustrado na figura 4.3. ?
8
Resolver este cálculo tem certas características
que são inerentes ao problema. Por exemplo, você
precisa saber o teorema de Pitágoras (a^2 + b^2 = c^2) 6
para resolvê-lo, e você tem que ser capaz de calcular primeiro o
quadrados de 8 e 6 e, em seguida, a raiz quadrada da soma Figura 4.3 Um problema de geometria no
qual os comprimentos de dois lados de
dos resultados. Porque não há outra maneira de resolver o problema
um triângulo são dados e o terceiro
problema ou para simplificar essas etapas, essa carga é intrínseca precisa ser calculado. Este pode ser um
ao problema. Na programação, muitas vezes usamos o problema difícil de resolver, dependendo
do seu conhecimento prévio. No entanto,
termo complexidade inerente para descrever essas
o problema em si não pode ser simplificado
aspectos de um problema. Na ciência cognitiva, dizemos que
sem alterá-lo.
esses aspectos causam carga cognitiva do tipo intrínseco.

CARGA COGNITIVA EXTRAORDINÁRIA AO LER O CÓDIGO


Além da carga natural e intrínseca que os problemas podem causar no cérebro, há
também carga cognitiva que se soma aos problemas, muitas vezes por acidente. Por exemplo, na figura
4.4, a mesma pergunta sobre encontrar o comprimento da hipotenusa é formulada em um
maneira diferente, exigindo que façamos uma conexão mental entre os rótulos para o
dois lados do triângulo, cujos comprimentos e valores são conhecidos. Este trabalho adicional
resulta em uma maior carga cognitiva estranha.
Machine Translated by Google

50 CAPÍTULO 4 Como ler código complexo

Considere estes valores para a e b:

a=8
?
uma b=6
Figura 4.4 Essa maneira de encontrar o
comprimento do terceiro lado de um triângulo
b incorre em uma carga cognitiva externa mais alta nessa forma.

Resolver o problema não se tornou realmente mais difícil - ainda precisamos lembrar e
aplicar o teorema de Pitágoras. No entanto, nossos cérebros precisam trabalhar mais em tarefas
estranhas: conectar a ao valor 8 e b ao valor 6. Na programação, pensamos
desse tipo de carga estranha como semelhante à complexidade acidental: aspectos de um programa
que tornam um problema mais difícil do que precisa ser.
O que cria carga estranha não é o mesmo para todos os programadores. O mais
experiência que você tem usando um determinado conceito, menos carga cognitiva ele cria para você.
Por exemplo, os dois exemplos de código Python na listagem a seguir são equivalentes em termos
computacionais.

Listagem 4.2 Duas versões de um programa Python para selecionar todos os itens acima de 10

above_ten = [a para a em itens se a > 10]

acima_dez = []
para um nos itens:
se a > 10: new_items.append(a)

Como os dois trechos de código resolvem o mesmo problema, eles compartilham o mesmo
carga cognitiva intrínseca. No entanto, se eles representam a mesma carga cognitiva estranha para
você depende de seu conhecimento prévio: se você não estiver familiarizado com as compreensões de
lista, então a carga estranha causada pelo primeiro exemplo será muito
maior do que para alguém que é experiente em usá-los.

EXERCÍCIO 4.1 Da próxima vez que você ler um código desconhecido, tente monitorar seu
própria carga cognitiva. Quando o código é difícil de processar e você sente a necessidade
para fazer anotações ou seguir a execução passo a passo, é provável que você esteja
experimentando uma alta carga cognitiva.

Quando você experimenta uma alta carga cognitiva, vale a pena examinar quais
partes do código estão criando os diferentes tipos de carga cognitiva. Você pode
use a tabela a seguir para analisar isso.
Machine Translated by Google

Técnicas para reduzir a carga cognitiva 51

Linhas de código Carga cognitiva intrínseca Carga cognitiva estranha

4.2 Técnicas para reduzir a carga cognitiva


Agora que você conhece as diversas maneiras pelas quais o código pode sobrecarregar a memória de trabalho,
é hora de direcionar nossa atenção para formas de diminuir a carga cognitiva. O restante de
este capítulo discute três métodos que tornarão mais fácil para você ler
código. A primeira técnica é aquela com a qual você já deve estar familiarizado, embora em um contexto
diferente: refatoração.

4.2.1 Refatoração
Uma refatoração é uma transformação de código que melhora sua estrutura interna, mas não
não altera o comportamento externo do código. Por exemplo, se um determinado bloco de código for
muito longo, você pode querer dividir a lógica em várias funções, ou se a base de código
exibe duplicação, você pode querer refatorá-lo para reunir todo o código duplicado em
um lugar para fácil reutilização. Na listagem de código Python a seguir, por exemplo, o
cálculo repetido poderia ser colocado em um método.

Listagem 4.3 Código Python que repete o mesmo cálculo duas vezes

vat_1 = Vat(nível da água = 10, raio = 1)


volume_vat_1 = math.pi * vat_1.radius **2 * vat_1. nível de água
print(volume_vat_1)

vat_1 = Vat(nível da água = 25, raio = 3)


volume_vat_2 = math.pi * vat_2. raio **2 * vat_2. nível de água
print(volume_vat_2)

Na maioria dos casos, a refatoração é feita para facilitar a manutenção do código resultante. Por
Por exemplo, eliminar o código duplicado significa que as alterações nesse código só precisam ser
feito em um local.

Mas o código que é mais sustentável em geral nem sempre é mais legível agora. Por
Por exemplo, considere um pedaço de código que contém muitas chamadas de método e, portanto, depende
no código espalhado por muitos lugares diferentes em um arquivo ou mesmo em vários arquivos. que
arquitetura pode ser mais sustentável porque toda a lógica tem seu próprio método.
No entanto, esse código deslocalizado também pode ser mais difícil para sua memória de trabalho porque
você terá que rolar ou procurar por definições de função em locais diferentes.
Machine Translated by Google

52 CAPÍTULO 4 Como ler código complexo

Portanto, às vezes você pode querer refatorar o código não para torná-lo mais sustentável a longo
prazo, mas mais legível para você naquele momento. Nós definimos
tal refatoração como uma refatoração cognitiva. Uma refatoração cognitiva é uma mudança em um código
base que não altera seu comportamento externo, semelhante a uma refatoração regular. No entanto, o
objetivo de uma refatoração cognitiva não é tornar o código mais sustentável,
mas para torná-lo mais legível para o leitor atual no momento atual.
Uma refatoração cognitiva às vezes pode envolver refatoração reversa, o que diminui
manutenibilidade, como inlining - pegando a implementação de um método e copiando
o corpo da função diretamente no site da chamada. Alguns IDEs podem realizar essa refatoração
automaticamente. O código embutido pode ser especialmente útil quando o nome de um método é
não muito revelador, como calcular() ou transform(). Ao ler uma chamada para um
método com um nome vago, você precisará gastar algum tempo na frente para entender
o que o método faz — e provavelmente levará várias exposições antes que a funcionalidade do método
seja armazenada em seu LTM.
Inline o método reduz sua carga cognitiva estranha e pode ajudá-lo
compreender o código que chama o método. Além disso, estudar o código do
método em si pode ajudá-lo a entender o código, o que pode ser mais fácil com mais
contexto. Dentro do novo contexto, você também poderá escolher um nome melhor para
o método.

Alternativamente, você pode querer reordenar métodos dentro do código—por exemplo,


o código pode ser mais fácil de ler se a definição de um método aparecer perto do primeiro método
ligar. É claro que muitos IDEs hoje em dia têm atalhos para navegar pelas definições de métodos e
funções, mas usar essa função também ocupa um pouco de memória de trabalho e
pode, assim, causar carga cognitiva estranha adicional.
As refatorações cognitivas geralmente são destinadas a uma pessoa, porque o que é compreensível
depende de seu próprio conhecimento prévio. Em muitos casos, as refatorações cognitivas são temporárias,
apenas para permitir que você entenda o código e podem ser roladas
de volta assim que sua compreensão estiver solidificada.
Embora esse processo possa ter sido um grande aborrecimento há alguns anos, os sistemas de
controle de versão agora são usados para a maioria das bases de código e integrados à maioria dos IDEs, que
torna relativamente fácil iniciar uma ramificação de “entendimento” local onde você executa o
alterações necessárias para compreender o código. E se algumas de suas refatorações resultarem
para serem valiosos em um sentido mais amplo, eles podem ser mesclados com relativa facilidade.

4.2.2 Substituindo construções de linguagem desconhecidas

O restante deste capítulo abrange técnicas que podem ajudá-lo a combater os três
possíveis fontes de confusão ao ler o código (falta de conhecimento, informação,
e poder de processamento). Se o código que você está lendo contém conceitos de programação
você não está familiarizado, você está lidando com a falta de conhecimento. Começaremos com um
técnica que pode ser útil nesses casos.
Em algumas situações, as construções desconhecidas com as quais você está trabalhando podem ser
expressa de uma forma diferente e mais familiar. Por exemplo, muitos programas modernos
Machine Translated by Google

Técnicas para reduzir a carga cognitiva 53

linguagens (como Java e C#) suportam funções anônimas, geralmente chamadas de lambdas.
Lambdas são funções que não precisam receber um nome (portanto, “anônimas”).
Outro exemplo é uma compreensão de lista em Python. Lambdas e compreensões de lista são
ótimas maneiras de tornar o código mais curto e mais legível, mas muitos programadores não
estão familiarizados com eles e os lêem com menos facilidade do que leriam um loop for ou while.

Se o código que você está lendo ou escrevendo for simples e direto, lambdas ou compreensão
de listas podem não representar um problema, mas se você estiver trabalhando com código mais
complexo, essas estruturas avançadas podem sobrecarregar sua memória de trabalho. Construções
de linguagem menos familiares aumentam a carga cognitiva estranha em sua memória de trabalho,
portanto, quando você está lendo um código complexo, pode ser benéfico para sua memória de
trabalho não ter que lidar com isso.
Embora as construções de linguagem precisas que você pode querer substituir sejam, é claro,
dependentes de seu próprio conhecimento prévio, normalmente há duas razões para substituir o
código para diminuir sua carga cognitiva: primeiro porque essas construções são conhecidas por
serem confusas e, segundo, porque eles têm um equivalente claro que é mais básico. Ambas as
condições se aplicam a lambdas e compreensões de lista, portanto, esses são bons exemplos a
serem usados para demonstrar essa técnica. Pode ser útil traduzi-los em um loop for ou while para
diminuir a carga cognitiva até que você compreenda melhor o que o código está fazendo. Os
operadores ternários também são bons candidatos para tal refatoração.

LAMBDAS
O código Java na listagem a seguir é um exemplo de uma função anônima usada como parâmetro
para uma função filter() . Se você estiver familiarizado com o uso de lambdas, este código será
bastante fácil de seguir.

Listagem 4.4 Uma função filter() em Java que recebe uma função anônima como argumento

Opcional<Produto> produto = productList.stream(). filter(p -> p.getId() == id).


encontrarPrimeiro();

No entanto, se lambdas são novos para você, esse código pode causar muita carga cognitiva
estranha. Se você sentir que está tendo problemas com a expressão lambda, você pode
simplesmente reescrever o código para usar uma função regular temporariamente, como no exemplo a seguir.

Listagem 4.5 Uma função filter() em Java que usa uma função tradicional como argumento

public static class Toetsie implementa Predicate <Product> { private int id;

Toetsie(int id){ this.id = id;

teste booleano(Produto p){


Machine Translated by Google

54 CAPÍTULO 4 Como ler código complexo

return p.getID() == this.id;

}}

Opcional<Produto> produto = productList.stream(). filter(novo Toetsie(id)). encontrarPrimeiro();

LISTA DE COMPREENSÕES

Python suporta uma estrutura sintática chamada compreensão de lista, que pode criar listas com
base em outras listas. Por exemplo, você pode usar o código a seguir para criar uma lista de nomes
com base em uma lista de clientes.

Listagem 4.6 Uma compreensão de lista em Python transformando uma lista em outra lista

customer_names = [c.first_name para c em clientes]

As compreensões de lista também podem usar filtros que as tornam um pouco mais complexas,
por exemplo, para criar uma lista de nomes de clientes com mais de 50 anos, conforme mostrado
na próxima listagem.

Listagem 4.7 Uma compreensão de lista em Python usando um filtro

customer_names =
[c.first_name para c em clientes se c.idade > 50]

Embora este código possa ser fácil de ler para alguém que está acostumado a listar compreensões,
para alguém que não está (ou mesmo alguém que está, se estiver embutido em um pedaço de
código complexo), pode causar muita pressão sobre a memória de trabalho. Quando isso acontecer,
você pode transformar a compreensão da lista em um loop for para facilitar a compreensão.

Listagem 4.8 Um loop for em Python transformando uma lista em outra lista usando um filtro

customer_names = []

para c em clientes:
se c.idade > 50:
customer_names.append(c.first_name)

OPERADORES TERNIÁRIOS

Muitas linguagens suportam operadores ternários, que são uma abreviação de instruções if. Eles
normalmente têm a forma de uma condição seguida pelo resultado quando a condição for
verdadeira e, em seguida, o resultado quando a condição for falsa. Por exemplo, a listagem 4.9 é
uma linha de código JavaScript que verifica se a variável booleana isMember é verdadeira usando
um operador ternário. Se isMember for true, o operador ternário retornará $ 2,00; se não, ele
retorna $ 10,00.
Machine Translated by Google

Técnicas para reduzir a carga cognitiva 55

Listando o código JavaScript 4.9 usando um operador ternário

éMembro? '$2,00': '$10,00'

Algumas linguagens, como Python, suportam operações ternárias em uma ordem diferente, dando
primeiro o resultado quando a condição for verdadeira, seguida pela condição e então a
resultado quando a condição for falsa. O exemplo a seguir é uma linha de código Python que
verifica se a variável booleana isMember é verdadeira usando um operador ternário. Como em
no exemplo JavaScript, se isMember for true, o operador ternário retornará $ 2,00 e se
não é, ele retorna $ 10,00.

Listando o código Python 4.10 usando um operador ternário

'$2,00' if is_member else '$10,00'

Conceitualmente, um operador ternário não é difícil de entender; como um programador profissional,


você provavelmente está familiarizado com código condicional. No entanto, o fato de a operação
ser colocada em uma linha ou de a ordem dos argumentos ser diferente de uma
A instrução if tradicional pode fazer com que o código crie muito
carga para você.
Para algumas pessoas, as refatorações descritas nas seções anteriores podem parecer
estranho ou errado. Você pode acreditar que encurtar o código com um lambda ou um ternário é
sempre preferível porque é mais legível, e você pode se opor à ideia de
refatorando o código para um estado pior. No entanto, como você viu neste capítulo e anteriormente
neste livro, “legível” está realmente nos olhos de quem vê. Se você está familiarizado com
aberturas de xadrez, é fácil lembrá-las; da mesma forma, se você estiver familiarizado com o uso
de ternários, é fácil ler o código que os contém. O que é fácil de ler depende do seu
conhecimento prévio, e não há vergonha em se ajudar a entender o código traduzindo-o para uma
forma mais familiar.
Dependendo da sua base de código, no entanto, você pode querer reverter as alterações
você fez no código para fins de legibilidade quando tiver certeza de entendê-lo.
Se você é novo em uma equipe e é o único que não está familiarizado com compreensões de lista,
você vai querer deixá-los no lugar e reverter suas refatorações.

4.2.3 Sinônimos de código são ótimas adições a um baralho de flashcard


Embora não haja vergonha em alterar o código temporariamente para ajudar na compreensão, isso
aponta para uma limitação em sua compreensão. No capítulo 3, você aprendeu sobre como criar
um baralho de flashcards para usar como auxiliar de aprendizado e memória, com código de um lado
e um prompt textual do outro. Por exemplo, um cartão em loops for poderia ter “print
todos os números entre 0 e 10 em C++” de um lado e o código C++ correspondente
(mostrado na listagem a seguir) por outro.
Machine Translated by Google

56 CAPÍTULO 4 Como ler código complexo

Listagem 4.11 Código C++ para imprimir os números entre 0 e 10


for (int i = 0; i <= 10; i = i + 1) {
cout << i << "\n";
}

Se você costuma ter dificuldades com, por exemplo, compreensão de listas, considere adicionar
algumas cartas dessa construção de programação para o seu baralho. Para conceitos de programação mais
avançados como esses, pode funcionar melhor ter código em ambos os lados da
flashcard em vez de uma explicação de texto - ou seja, você pode ter o código vanilla em um
lado, e por outro o código equivalente usando o conceito avançado, como um ternário ou lambda.

4.3 Auxílios de memória para usar quando sua memória de trabalho está sobrecarregada
A seção anterior introduziu uma técnica para reduzir a carga cognitiva que o código
pode criar, refatorando-o para uma forma mais familiar. No entanto, mesmo em seu estado refatorado,
código ainda pode sobrecarregar sua memória de trabalho se sua estrutura for muito complexa. Lá
são duas maneiras de código com uma estrutura complicada pode sobrecarregar a memória de trabalho.
Primeiro, você pode não saber exatamente quais partes do código você precisa ler. Isto
faz com que você leia mais do código do que o necessário, o que pode ser mais do que o seu
memória de trabalho é capaz de processar.
Segundo, com código altamente conectado, seu cérebro está tentando fazer duas coisas ao mesmo tempo.
ao mesmo tempo: entender linhas individuais de código e entender a estrutura de

o código para decidir onde continuar lendo. Por exemplo, quando você encontra um
chamada de método para a qual você não conhece a funcionalidade exata, talvez seja necessário
localize e leia o método antes de continuar lendo o código no site da chamada.
Se você já leu o mesmo pedaço de código cinco vezes seguidas sem fazer
progresso, você provavelmente não sabia em quais partes do código focar ou em que ordem.
Você pode ter entendido cada linha de código individualmente, mas não
compreensão do quadro maior. Quando você atinge os limites de sua memória de trabalho, pode usar um
auxílio de memória para ajudá-lo a se concentrar nas partes certas do código.

4.3.1 Criando um gráfico de dependência


Criar um gráfico de dependência em cima de seu código pode ajudá-lo a entender o
fluxo e ler o código seguindo o fluxo lógico. Para esta técnica, eu
aconselhá-lo a imprimir o código ou convertê-lo em PDF e abri-lo em um tablet para que você
pode fazer anotações digitalmente. Siga estas etapas para anotar o código para dar suporte
sua memória ao processá-lo:

1 Circule todas as variáveis.


Depois de ter o código em um formulário que você pode anotar, comece encontrando todos os
variáveis e circulando-as, conforme mostrado na figura 4.5.
Machine Translated by Google

Auxílios de memória para usar quando sua memória de trabalho está sobrecarregada 57

Figura 4.5 Código no qual todas as variáveis são circuladas para auxiliar no entendimento

2 Vincule variáveis semelhantes.

Depois de localizar todas as variáveis, desenhe linhas entre as ocorrências da


mesma variável, conforme ilustrado na figura 4.6. Isso ajuda você a entender onde
os dados são usados no programa. Dependendo do código, você também pode
querer vincular variáveis semelhantes (por exemplo, acessos a uma lista, como em
clientes[0] e clientes[i]).
Vincular todas as variáveis ajudará você a ler o código, pois em vez de procurar outras ocorrências,
você pode simplesmente seguir as linhas. Isso reduz sua carga cognitiva e, portanto, libera memória de
trabalho para você se concentrar na funcionalidade do código.

3 Circule todas as chamadas de método/função.

Depois de localizar todas as variáveis, concentre-se nos métodos e funções no código. Circule-os em
uma cor diferente.
Machine Translated by Google

58 CAPÍTULO 4 Como ler código complexo

Figura 4.6 Código no qual todas as variáveis são circuladas e vinculadas às suas outras
ocorrências para auxiliar no entendimento

4 Vincule métodos/funções às suas definições.


Desenhe uma linha entre cada definição de função ou método e os locais

onde são invocados. Concentre atenção especial em métodos com apenas uma invocação, porque
esses métodos são candidatos a serem alinhados com uma refatoração,
como explicado anteriormente neste capítulo.
5 Circule todas as instâncias de classes.

Depois de localizar as variáveis e funções, concentre-se nas classes. Circule todos


instâncias de classes em uma terceira cor.
6 Desenhe um link entre as classes e suas instâncias.

Como etapa final no exame do código, vincule instâncias da mesma classe aos seus
definição se essa definição estiver presente no código. Se a definição não estiver presente, você
pode vincular as instâncias da mesma classe umas às outras.

O padrão colorido que você criou usando as seis etapas anteriores indica o fluxo de
o código e pode ser usado como um auxílio na sua leitura. Agora você tem uma referência que pode consultar
para obter informações sobre a estrutura do código, economizando o esforço de, por exemplo,
Machine Translated by Google

Auxílios de memória para usar quando sua memória de trabalho está sobrecarregada 59

ter que procurar definições e também decifrar o significado do código, que


pode sobrecarregar sua memória de trabalho. Você pode começar em um ponto de entrada do código, como
o método main() e leia-o a partir daí. Sempre que você encontrar um link para um
chamada de método ou instanciação de classe, você pode seguir a linha que desenhou e continuar
lendo no lugar certo imediatamente, evitando perder tempo pesquisando ou lendo
mais código do que o necessário.

4.3.2 Usando uma tabela de estados

Mesmo quando o código é refatorado para a forma mais fácil possível alinhada com o seu
conhecimento e com todas as dependências marcadas, ainda pode ser confuso. Às vezes o
causa da confusão não é a estrutura do código, mas os cálculos que ele faz. Esta é uma questão de falta
de poder de processamento.
Por exemplo, vamos revisitar o código BASIC do capítulo 1 que converte o número
N em uma representação binária. Neste programa as variáveis influenciam umas às outras fortemente, por
isso é necessário um exame detalhado de seus valores para compreendê-lo. Você pode usar um
auxílio de memória, como um gráfico de dependência para código como este que executa cálculos
complicados, mas há outra ferramenta que pode ajudar com códigos pesados de cálculo: uma tabela de estados.
Uma tabela de estado se concentra nos valores das variáveis em vez da estrutura do código.

Possui colunas para cada variável e linhas para cada etapa do código. Dê outra olhada
em nosso exemplo de programa BASIC na próxima listagem. É confuso porque você não pode ver
todos os cálculos intermediários e seus efeitos nas diferentes variáveis.

Listagem 4.12 Código BASIC que converte um número N em sua representação binária

1 LET N2 = ABS (INT (N))


""
2 LET B$ =
3 PARA N1 = N2 PARA 0 PASSO 0 N N2 B$ N1
4 LET N2 = INT (N1 / 2) Iniciar77 7
5 LET B$ = STR$ (N1 - N2 * 2) + B$
Ciclo 1 3 31
6 DEIXE N1 = N2
7 PRÓXIMO N1 Ciclo 2
8 IMPRIMIR B$
9 DEVOLUÇÃO Figura 4.7 Um exemplo de uma tabela
de estado parcial do código BASIC
Se você precisa entender um código como este com muitos cálculos para calcular a representação binária

interconectados, você pode usar um auxílio de memória como a tabela de de um número

estado parcial mostrada na figura 4.7.


Siga estas etapas para criar uma tabela de estado:
1 Faça uma lista de todas as variáveis.

Se você já criou um gráfico de dependência para este programa, como


descrito na seção anterior, será fácil listar as variáveis porque
você terá circulado todos eles na mesma cor.

2 Crie uma tabela e dê a cada variável sua própria coluna.


Na tabela de estados, cada variável receberá uma coluna na qual seu intermediário
valores podem ser registrados, conforme mostrado na figura 4.7.
Machine Translated by Google

60 CAPÍTULO 4 Como ler código complexo

3 Adicione uma linha à tabela para cada parte distinta da execução do código.
O código que contém cálculos complexos provavelmente também conterá algumas dependências
complexas, como um loop dependendo de um cálculo ou um if complicado
declaração. As linhas na tabela de estado representam partes separadas das dependências. Por
Por exemplo, como mostrado na figura 4.7, uma linha pode representar uma iteração em um loop,
precedida pelo código de inicialização. Alternativamente, uma linha pode representar uma ramificação em um
grande if, ou simplesmente um grupo de linhas de código coerentes. Em código extremamente
complexo e conciso, uma linha na tabela pode até representar uma linha de código.
4 Execute cada parte do código e anote o valor que cada variável tem
na linha e coluna corretas.

Depois de preparar a tabela, percorra o código e calcule o novo valor de cada variável para cada
linha na tabela de estados. O processo de
o código executado mentalmente é chamado de rastreamento ou compilação cognitiva. Ao rastrear o
código usando uma tabela de estados, pode ser tentador pular algumas variáveis e apenas preencher
parte da mesa, mas tente resistir a essa tentação. Trabalhar com ele meticulosamente o ajudará a
obter uma compreensão mais profunda do código e o resultado
table irá suportar sua memória de trabalho sobrecarregada. Em uma segunda leitura do
programa você pode usar a tabela como referência, permitindo que você se concentre no
coerência do programa e não nos cálculos detalhados.

Um aplicativo para apoiar a memória de trabalho


A criação manual de visualizações para apoiar sua memória de trabalho tem muito valor
porque força você a examinar o código em detalhes. No entanto, essas visualizações
também pode ser criado automaticamente. Um programa elegante para este propósito é o Python
Tutor, criado por Philip Guo, professor de ciências cognitivas da Universidade de Cali fornia, San Diego.
PythonTutor, que agora está disponível para muitas linguagens de programação além do Python, visualiza a
execução de um programa. Por exemplo, o
a figura a seguir mostra que o Python armazena números inteiros e listas de forma diferente; para um inteiro,
o valor é armazenado, enquanto para uma lista, um sistema semelhante a ponteiro é usado.

Figura 4.8 Tutor Python mostrando a diferença entre armazenar o inteiro x e seu valor
diretamente e armazenar uma fruta de lista com um ponteiro
Machine Translated by Google

Auxílios de memória para usar quando sua memória de trabalho está sobrecarregada 61

Pesquisas explorando o uso do Python Tutor na educação1 mostraram que os alunos


levam um tempo para se acostumar a trabalhar com o programa, mas que é útil, especialmente
ao depurar.

1
4.3.3 Combinando gráficos de dependência e tabelas de estado
Esta seção e a seção 4.2 descrevem duas técnicas para apoiar sua memória de trabalho
ao ler o código descarregando algumas informações sobre o código no papel: desenhando um gráfico de
dependência e criando uma tabela de estados. Essas técnicas focam em diferentes partes do código:
enquanto o gráfico de dependência chama sua atenção para como o
código é organizado, a tabela de estados captura os cálculos no código. Ao explorar um código
desconhecido, você pode usar ambos os exercícios para obter uma visão completa de seu funcionamento
interno e usar como auxiliares de memória ao ler o código após completá-los.

EXERCÍCIO 4.2 Seguindo os passos descritos nas seções anteriores, crie ambos
um gráfico de dependência e uma tabela de estado para cada um dos seguintes programas Java.

Programa 1

Cálculos de classe pública {


public static void main(String[] args) {
char[] chars = {'a', 'b', 'c', 'd'};
// procurando por bba
calcular(caracteres, 3, i -> i[0] == 1 && i[1] == 1 && i[2] == 0);
}
static void calcular(char[] a, int k, Predicate<int[]> decider) {
int n = a.comprimento;
se (k < 1 || k > n)
throw new IllegalArgumentException("Proibido");

int[] índices = new int[n];


int total = (int) Math.pow(n, k);

while (total--> 0) {
for (int i = 0; i < n - (n - k); i++)
System.out.print(a[indexes[i]]);
System.out.println();

if (decider.test(índices))
pausa;

for (int i = 0; i < n; i++) {


if (índices[i] >= n - 1) {
índices[i] = 0;
} senão {
índices[i]++;

1
Veja “The Use of Python Tutor on Programming Laboratory Session: Student Perspectives” de Oscar Karnalim
e Mewati Ayub (2017), https://kinetik.umm.ac.id/index.php/kinetik/article/view/442.
Machine Translated by Google

62 CAPÍTULO 4 Como ler código complexo

pausa;
}
}
}
}
}

Programa 2

public class App { private


static final int WIDTH = 81; private estático final int ALTURA
= 5;

linhas de caractere estático privado[][]; static


{ linhas = new char[HEIGHT][WIDTH]; for (int i =
0; i < ALTURA; i++) {

for (int j = 0; j < WIDTH; j++) { linhas[i][j] = '*';

}
}
}

private static void show(int start, int len, int index) {


int seg = len/3; if (seg == 0)
return; for (int i = índice; i <
ALTURA; i++) {
for (int j = inicio + seg; j < inicio + seg linhas[i][j] = ' '; * 2; j++) {

} show(início, seg, índice + 1); show(início +


seg * 2, seg, índice + 1);
}

public static void main(String[] args) {


show(0, LARGURA, 1); for
(int i = 0; i < ALTURA; i++) {
for (int j = 0; j < LARGURA; j++) {
System.out.print(linhas[i][j]);
}
System.out.println();
}
}
}

Resumo ÿ
A carga cognitiva representa o limite do que a memória de trabalho pode processar.
Quando você experimenta muita carga cognitiva, não consegue processar o código
corretamente.
ÿ Existem dois tipos de carga cognitiva que são relevantes na programação: a carga
cognitiva intrínseca é criada pela complexidade inerente de um pedaço de código, enquanto
Machine Translated by Google

Resumo 63

carga cognitiva estranha é adicionada ao código acidentalmente (pela forma como


é apresentada) ou devido a lacunas no conhecimento da pessoa que lê o código.

ÿ A refatoração é uma maneira de reduzir a carga cognitiva estranha ao transformar o código


para alinhar melhor com o seu conhecimento anterior.
ÿ Criar um gráfico de dependência pode ajudá-lo a entender um
código interligado.

ÿ Criar uma tabela de estados contendo os valores intermediários das variáveis pode auxiliar na
leitura de código que é pesado em cálculos.
Machine Translated by Google
Machine Translated by Google

Parte 2

Ao pensar em código

Dentroparte 1, examinamos os papéis do STM, do LTM e da memória de


trabalho ao processar o código. Também analisamos o que sabemos sobre
aprender sintaxe e conceitos de programação e como apoiar seu cérebro ao ler
código.

Na parte 2, vamos nos concentrar não tanto na leitura de código, mas em pensar sobre
código: como entender profundamente os programas e evitar bugs no pensamento.
Machine Translated by Google
Machine Translated by Google

Alcançando uma profundidade

compreensão de código

Este capítulo abrange


ÿ Examinar os diferentes papéis que as variáveis podem
jogar em programas
ÿ Comparar o conhecimento superficial do código e a
compreensão da intenção do criador
ÿ Comparar a leitura e a aprendizagem da linguagem natural
para ler e aprender código
ÿ Explorar diferentes estratégias para obter uma compreensão
mais profunda do código

Anteriormente neste livro, discutimos o uso de flashcards e a prática repetida como técnicas para
aprender sintaxe, e abordamos estratégias para se familiarizar rapidamente com
novo código, como realçar variáveis e seus relacionamentos. Ao saber
sintaxe e entender as relações entre as variáveis é um passo importante
para entender o código, há questões mais profundas que desempenham um papel quando se pensa
sobre o código.

Quando você lê um pedaço de código desconhecido, pode ser difícil ver o que o código
está fazendo. Para usar um termo cognitivo apresentado anteriormente neste livro, você pode dizer que

67
Machine Translated by Google

68 CAPÍTULO 5 Alcançando uma compreensão mais profunda do código

ao ler um código desconhecido, sua carga cognitiva é alta. Vimos que a carga cognitiva pode ser reduzida
substancialmente aprendendo sintaxe e novos conceitos de programação e reescrevendo o código.

Uma vez que você tenha uma boa compreensão do que o código está fazendo, o próximo passo é
pense sobre o código em mãos com mais profundidade. Como foi criado? Onde você pode
adicionar um novo recurso? Quais são algumas alternativas de projeto possíveis?
Nos capítulos anteriores, falamos sobre esquemas, ou como as memórias são organizadas em
o cérebro. As memórias não são armazenadas separadamente, mas possuem links para outras memórias. Você
pode tirar proveito dessas conexões ao raciocinar sobre código porque as memórias armazenadas em seu LTM
ajudam você a criar pedaços em sua memória de trabalho que podem ajudar
você pensa em código.
Pensar em código é o tópico deste capítulo, no qual iremos
uma compreensão mais profunda do código. Abordaremos três estratégias para refletir sobre o código em um
nível mais profundo, incluindo métodos para raciocinar sobre as idéias, pensamentos e decisões de
o criador do código. Primeiro, examinaremos uma estrutura que o ajudará a raciocinar sobre
código. Em seguida, discutiremos diferentes níveis de compreensão e algumas técnicas para
indo mais fundo. Por fim, vamos nos aprofundar em algumas estratégias provenientes da leitura natural
linguagem que pode ajudar na leitura do código. Pesquisas recentes indicam que as habilidades que
necessidade de leitura de código e as habilidades que usamos para ler linguagem natural são fortemente
relacionados, o que significa que nós programadores podemos aprender muito com a linguagem natural
é lido para uma compreensão mais profunda.

5.1 Funções da estrutura de variáveis


Ao raciocinar sobre código, fica claro que as variáveis desempenham um papel central. Compreender que tipos de
variáveis de informação contêm é a chave para ser capaz de raciocinar e
fazer alterações no código. Se você não entende o que uma determinada variável deve
representar, pensar no código será tremendamente difícil. É por isso que bons nomes de variáveis podem servir
como balizas, ajudando-nos a obter uma compreensão mais profunda do código
que estamos lendo.
Segundo o professor Jorma Sajaniemi, da Universidade da Finlândia Oriental, o
razão pela qual as variáveis são difíceis de entender é que a maioria dos programadores não tem um bom
esquema em seu LTM para relacionar variáveis. Sajaniemi argumenta que tendemos a usar
pedaços que abrangem muito, como “variável” ou “inteiro”, ou são muito pequenos,
como um nome de variável específico como number_of_customers. Em vez disso, os programadores
precisa de algo no meio, o que o motivou a projetar os papéis do trabalho de quadro de variáveis. A função de
uma variável indica o que ela faz dentro do programa.

5.1.1 Diferentes variáveis fazem coisas diferentes


Como exemplo das diferentes funções que as variáveis podem desempenhar, considere o seguinte Python
programa. A função prime_factors(n) no código retorna o número de primos
fatores em que n pode ser separado:
Machine Translated by Google

Funções da estrutura de variáveis 69

upperbound = int(input('Upper bound?')) max_prime_factors


= 0 for counter in range(upperbound):

fatores = prime_factors(counter) se fatores >


max_prime_factors: max_prime_factors = fatores

Este programa contém quatro variáveis: limite superior, contador, fatores e fatores
max_prime_. No entanto, se simplesmente descrevermos este programa como tendo
quatro variáveis, não será muito útil para compreender o programa; isso é abstrato demais.
Observar os nomes das variáveis pode ajudar um pouco, mas não explica tudo. counter, por exemplo,
ainda é muito genérico. Este é um número estático de coisas ou muda no programa? Examinar os
papéis que cada uma das quatro variáveis desempenha pode ajudar.

Neste programa, o usuário é solicitado a informar um valor, que é armazenado no limite superior
da variável. Depois disso, um loop será executado até atingir esse limite superior no contador de
variáveis. Os fatores variáveis mantêm temporariamente o número de fatores primos para o valor atual
do contador. Por fim, a variável max_prime_factors representa o maior número encontrado na execução
do loop.
A estrutura de papéis de variáveis captura essa diferença no comportamento dessas variáveis. A
variável limite superior desempenha o papel de um detentor mais recente: ela armazena o limite
superior inserido mais recentemente. counter, por outro lado, é um stepper, que itera através de um
loop. max_prime_factors é o titular mais procurado; ele armazena um valor que está sendo procurado.
Os fatores variáveis são titulares mais recentes; ele armazena o número mais recente de fatores
primos. Na seção a seguir, explicarei essas funções e as outras na estrutura com mais detalhes.

5.1.2 Onze papéis para cobrir quase todas as variáveis

Como o exemplo anterior mostra, os papéis que as variáveis desempenham são comuns. Muitos
programas têm uma variável que é um stepper ou um suporte mais procurado. Na verdade, Sajaniemi
argumenta que com apenas 11 papéis, você pode descrever quase todas as variáveis:

ÿ Valor fixo—Uma variável cujo valor não muda após a inicialização desempenha o papel de um
valor fixo. Este pode ser um valor constante se a linguagem de programação que você está
usando permite que os valores sejam fixos, ou pode ser uma variável que é inicializada uma
vez e depois não é alterada. Exemplos de variáveis de valor fixo incluem constantes matemáticas
como pi, ou dados lidos de um arquivo ou banco de dados. ÿ Stepper —Ao iterar em um loop,
sempre há uma variável percorrendo uma lista de valores. Esse é o papel do stepper, cujo valor
pode ser previsto assim que a sucessão se inicia. Isso pode ser um inteiro, como a iteração
canônica em um loop for, mas steppers mais complicados também são possíveis, como size =
size / 2 em uma pesquisa binária, onde o tamanho do array a ser pesquisado é cortado pela
metade em cada
iteração.
Machine Translated by Google

70 CAPÍTULO 5 Alcançando uma compreensão mais profunda do código

1
ÿ Bandeira —Uma variável usada para indicar que algo aconteceu ou é o caso.

Exemplos típicos são is_set, is_available ou is_error. Os sinalizadores geralmente são bool eans, mas
podem ser inteiros ou até mesmo strings.
ÿ Walker —Um walker percorre uma estrutura de dados, semelhante a um stepper. A diferença
está na forma como a estrutura de dados é percorrida. Um stepper sempre itera sobre uma lista
de valores que são conhecidos de antemão, como em um loop for em Python: for i in
intervalo(0, n). Um walker, por outro lado, é uma variável que percorre um
estrutura de uma maneira desconhecida antes do início do loop. Dependendo do
linguagem de programação, os caminhantes podem ser ponteiros ou índices inteiros. Os caminhantes podem
percorre listas, por exemplo, na pesquisa binária, mas percorre com mais frequência estruturas de dados
como uma pilha ou uma árvore. Exemplos de um andador são uma variável que está atravessando
uma lista encadeada para encontrar a posição onde um novo elemento deve ser adicionado ou um
índice de busca em uma árvore binária.
ÿ Detentor mais recente—Uma variável que contém o valor mais recente encontrado na
através de uma série de valores é um detentor mais recente. Por exemplo, pode armazenar
a última linha lida de um arquivo (line = file.readline()), ou uma cópia do
elemento de array referenciado pela última vez por um stepper (element = list[i]).
ÿ Detentor mais procurado —Muitas vezes, quando você está iterando sobre uma lista de valores, você está
fazendo isso para procurar um determinado valor. A variável que mantém esse valor, ou
o melhor valor encontrado até agora, é o que chamamos de titular mais procurado. Canônico
exemplos de um detentor mais procurado são uma variável que armazena um valor mínimo, um
valor máximo, ou o primeiro valor que atende a uma determinada condição.
ÿ Gatherer —Um coletor é uma variável que coleta dados e os agrega em um
valor. Esta pode ser uma variável que começa em zero e coleta valores durante a iteração
através de um loop, assim:

soma = 0
para i no intervalo (lista):
soma += lista[i]

Seu valor também pode, no entanto, ser calculado diretamente em linguagens funcionais ou linguagens que
englobem certos aspectos funcionais: funcional_total = soma(lista).

ÿ Container —Um container é qualquer estrutura de dados que contém vários elementos que
pode ser adicionado e removido. Exemplos de contêineres são listas, arrays, pilhas e
árvores.

ÿ Seguidor —Alguns algoritmos exigem que você acompanhe um valor anterior ou subsequente. Uma variável
neste papel é chamada de seguidor e é sempre acoplada a
outra variável. Exemplos de variáveis seguidoras são um ponteiro que aponta para um
elemento anterior em uma lista encadeada ao percorrer a lista, ou o índice inferior em uma
busca binária.

1
A estrutura de Sajaniemi especificamente chama isso de “bandeira de mão única”, mas acho que esse papel específico é muito restrito.
Machine Translated by Google

Papéis e paradigmas 71

ÿ Organizador—Às vezes, uma variável precisa ser transformada de alguma forma para
em processamento. Por exemplo, em alguns idiomas, você não pode acessar caracteres individuais em uma
string sem primeiro converter a string em uma matriz de caracteres, ou
pode querer armazenar uma versão ordenada de uma determinada lista. Esses são exemplos de organizadores,
que são variáveis que são usadas apenas para reorganizar ou armazenar valores de forma diferente. Muitas
vezes, são variáveis temporárias.
ÿ Temporária—Variáveis temporárias são variáveis que são usadas apenas brevemente e são
muitas vezes chamado de temp ou t. Estas variáveis podem ser usadas para trocar dados ou para armazenar o
resultado de uma computação que é usada várias vezes em um método ou função.

A Figura 5.1 apresenta uma visão geral das 11 funções de Sajaniemi e ajuda você a descobrir quais
papel que uma variável pode desempenhar.

sim
Valor constante? Valor fixo
não
sim Recipiente

Armazenamento temporário? Organizador


não Temporário
sim
Verificando? Bandeira

não

Repetição?
sim

Contando? Andador Atrasando?


de Passo Titular mais recente Seguidor

Acumulando? Escolhendo? Figura 5.1 Você pode usar este fluxograma


para ajudá-lo a determinar a função de uma
Coletor Titular mais procurado
variável em um pedaço de código.

5.2 Papéis e paradigmas


Os papéis não se restringem a um paradigma de programação específico, mas ocorrem em todos os paradigmas. Já
vimos no exemplo do coletor que os coletores também ocorrem
em linguagens funcionais. Você verá as variáveis desempenhando os papéis descritos na seção anterior na programação
orientada a objetos também. Por exemplo, considere o seguinte
classe Java:

classe pública Cachorro {


Nome da cadeia;
idade interna;
public Dog (String n) {
nome = n;
idade = 0;
}
aniversário nulo público () {
idade++;
}
}
Machine Translated by Google

72 CAPÍTULO 5 Alcançando uma compreensão mais profunda do código

As instâncias de Dog têm dois atributos: name e age. O valor do nome do atributo
não muda após a inicialização; é um valor fixo. O atributo age se comporta de maneira semelhante ao contador
de variáveis no programa Python que vimos anteriormente: ele avança
através de uma sequência conhecida começando em 0 e aumentando a cada aniversário, de modo que,
seu papel é stepper.

5.2.1 Benefícios das funções

Para a maioria dos programadores profissionais, os papéis no framework de Sajaniemi serão um pouco familiares
(talvez por outros nomes). Em vez de introduzir novos conceitos, o
O objetivo desta lista é fornecer a você um novo vocabulário para usar ao discutir variáveis.
Especialmente quando compartilhado entre uma equipe, a estrutura pode ser uma ótima maneira de melhorar
compreensão e comunicação sobre código.
Estar familiarizado com essas funções também pode trazer benefícios para os recém-chegados. Estudos têm
mostrou que esta estrutura pode ajudar os alunos a processar mentalmente o código-fonte e que
os alunos que usam os papéis da estrutura de variáveis superam aqueles que não usam.2 Um
razão pela qual é tão eficaz é que muitas vezes um grupo de papéis juntos caracteriza um certo
Tipo de programa. Por exemplo, um programa com um stepper e um suporte mais procurado
value é um programa de busca.

EXERCÍCIO 5.1 Este é um ótimo momento para praticar o uso dos papéis das variáveis
estrutura. Encontre algum código com o qual você não esteja familiarizado e examine o
variáveis tomando nota do seguinte para cada um:

ÿ O nome da variável

ÿ O tipo da variável
ÿ As operações nas quais a variável desempenha um papel
ÿ O papel da variável de acordo com a estrutura de papéis de variáveis de Sajaniemi

Preencha esta tabela para cada variável encontrada no código.

Nome variável Modelo Operações Função

Depois de preencher a tabela, reflita sobre suas decisões sobre o papel de cada
variável. Como você determinou o papel? Qual dos outros aspectos desempenhou
parte na sua decisão? Foi influenciado pelo nome da variável, suas operações, comentários no código
ou talvez sua própria experiência com o código?

2
Veja, por exemplo, “Um experimento sobre o uso de funções de variáveis no ensino de programação introdutória” por
Jorma Sajaniemi e Marja Kuittinen (2007), www.tandfonline.com/doi/full/10.1080/08993400500056563.
Machine Translated by Google

Papéis e paradigmas 73

DICAS PRÁTICAS PARA TRABALHAR COM PAPÉIS DE VARIÁVEIS


Ao ler um código totalmente desconhecido, acho que ajuda imprimir o código em
papel ou salvá-lo como um PDF que eu possa anotar. Eu percebo que pode parecer estranho ler o código
fora do IDE, e você certamente perderá alguns recursos, como poder
Valor fixo
pesquisar o código. No entanto, ser capaz de escrever notas pode
aprofundar seu pensamento Stepper

sobre o código, permitindo que você interaja com ele em um nível diferente. Bandeira

andador

Já fiz exercícios de código em papel com muitos programadores Titular mais recente
profissionais e, uma vez que passaram de suas primeiras inibições, todos
Titular mais procurado
acharam tremendamente valioso. De
Coletor
claro, para projetos maiores, talvez você não consiga imprimir todos
Recipiente
o código-fonte relevante, mas você pode começar com uma classe ou
Seguidor
parte do programa. Se a impressão do código não for viável
devido ao seu tamanho ou por outras razões práticas, muitos Organizador

as técnicas de anotações descritas aqui também podem ser Temporário


feito em um IDE usando comentários. Figura 5.2 Você pode criar um
Ao trabalhar no exercício 5.1, gosto de imprimir o conjunto de ícones correspondentes
codificar e marcar o papel de cada variável com um pouco aos 11 papéis que uma variável pode
jogar de acordo com a estrutura de
ícone, como mostrado na figura 5.2. Sajaniemi e usá-los para
Depois de memorizar os ícones, eles rapidamente marcar os papéis das variáveis em

tornar-se um forte auxiliar de memória. Para fazer a memorização deles código desconhecido. Estes são
os ícones que eu uso.
mais fácil, você pode criar um baralho de flashcards.
A Figura 5.3 mostra o exemplo de código Python anterior com os papéis das variáveis
anotado.

limite superior = int(input('Limite superior ?'))


Figura 5.3 Um trecho de código em Python,
max_prime_factors = 0
anotado com ícones que indicam os papéis das
para contador no intervalo (limite superior):
variáveis no programa.
fatores = fatores_primos(contador)
A variável de limite superior é um detentor
if fatores > max_prime_factors:
max_prime_factors = fatores 5 mais recente, counter é um stepper e
max_prime_factors é um detentor mais
procurado.

Ao escrever código, pode ser muito valioso colocar o nome da função na variável
nome, especialmente quando todas as pessoas que trabalham com o código estão familiarizadas com o conceito
de papéis. Embora possa tornar o nome da variável mais longo, transmite informações importantes
informações e poupa o leitor o esforço de descobrir o papel por si mesmo.

5.2.2 notação húngara


Os papéis da estrutura de variáveis podem ter lembrado você de algo chamado notação húngara. A ideia da
notação húngara é codificar o tipo de uma variável em sua
Machine Translated by Google

74 CAPÍTULO 5 Alcançando uma compreensão mais profunda do código

nome—por exemplo, strName é uma string que representa um nome e lDistance é um


longo que representa uma distância. Esta convenção decorre de linguagens que não possuem um tipo
sistema para codificar os tipos de variáveis.
A notação húngara foi descrita por Charles Simonyi em sua tese de doutorado de 1976
“Meta-programação: um método de produção de software” – que ainda é uma boa
ler. Simonyi passou a trabalhar para a Microsoft, onde liderou o desenvolvimento do Word
e Excel. Sua convenção de nomenclatura tornou-se o padrão para software desenvolvido por
Microsoft, e mais tarde para software desenvolvido em linguagens Microsoft como Visual Basic.
A notação húngara foi usada extensivamente pela primeira vez na Linguagem de Programação
Combinada Básica (BCPL), vista por muitos como um ancestral do C, na década de 1970. No
dias em que não havia IDEs com o IntelliSense, você não podia ver facilmente o tipo de um
variável no editor. Portanto, adicionar informações sobre o tipo de uma variável ao seu
name poderia melhorar a legibilidade de uma base de código. A compensação foi que fez o
nomes mais longos e, portanto, mais difíceis de ler, e quando um tipo teve que mudar, muitas variáveis
nomes podem ser potencialmente afetados. Hoje em dia, porque a maioria dos editores pode facilmente mostrar
o tipo de uma variável, a notação húngara não é vista como agregando valor em um idioma
com tipos, porque apenas torna os nomes das variáveis mais longos. Tipos de codificação em uma variável
nome como este não é mais uma prática comum, e hoje o uso do húngaro
notação é geralmente desaprovada.

APPS HÚNGARO VS. SISTEMA HÚNGARO


No entanto, simplesmente codificar tipos em nomes de variáveis não é de fato o que Simonyi propõe em
sua tese. Codificar os tipos de variáveis em seus nomes é o que chamamos agora
sistemas notação húngara.
A proposta de Simonyi era muito mais semântica por natureza. Hoje, é referido como Apps
notação húngara. No Apps húngaro, os prefixos têm um significado mais específico do que
apenas indicando os tipos de variáveis. Por exemplo, em sua tese, Simonyi sugere usar
cX para contar instâncias de X (para que cColors possa ser o número de cores em uma interface do usuário) e lX
para indicar o comprimento de uma matriz, como em lCustomers. A razão pela qual esta forma da
convenção é chamada Apps Húngaro é por causa do envolvimento de Simonyi com o Word e
Excel na Microsoft. A base de código do Excel contém muitas variáveis prefixadas rw ou col,
que são excelentes exemplos do bom uso da convenção. Os valores de linha e coluna serão inteiros, mas
para fins de legibilidade, é ótimo poder diferenciar
o nome a que se refere.

Por motivos que não são totalmente claros, a equipe do Windows também adotou a convenção, mas
apenas para tipos de dados, não semânticos. Joel Spolsky, que trabalhou no Excel antes
fundador do Stack Overflow, atribuiu a má interpretação da notação húngara
ao fato de Simonyi usar a palavra “tipo” em vez de “tipo” para explicar o papel do
o prefixo.3

3
“Making Wrong Code Look Wrong”, de Joel Sprosky, 11 de maio de 2005, www.joelonsoftware.com/2005/05/11/
fazendo-código-errado-parecer-errado/.
Machine Translated by Google

Adquirindo um conhecimento mais profundo dos programas 75

Se você olhar para o trabalho original de Simonyi, no entanto, sua explicação dos tipos aparece em
a mesma página como exemplos concretos, sem tipo, como cX para contagem. acho que é mais
provável que um pequeno grupo de pessoas, ou talvez apenas uma, simplesmente começou a usar o sistema
da maneira errada, e esse uso se espalhou. Como veremos com mais detalhes no capítulo 10,
as pessoas geralmente se apegam às convenções uma vez que estão no código. No entanto, veio a ser,
a forma errada de notação húngara foi popularizada no mundo Windows—
em grande parte pelo influente livro de Charles Petzold Programming Windows (Microsoft Press,
1998) - e então vieram as pessoas dizendo: "A notação húngara é considerada prejudicial",
E o resto é história.
No entanto, acho que ainda há muito valor nas ideias de Simonyi. Algumas das propostas defendidas
pelo Apps Húngaro são muito parecidas com os papéis na estrutura de Sajamieni. Por exemplo, Simonyi
usou o prefixo t para denotar um valor temporário, e também
proposto min e max como prefixos para os valores mínimos e máximos em uma matriz,
que são exemplos típicos dos valores mais procurados das funções do quadro de variáveis. É uma pena que
o principal benefício da notação húngara original - torná-la
mais fácil de ler o código porque menos esforço mental é necessário para raciocinar sobre o papel de um

variável - parece ter sido perdido por causa de um mal-entendido do objetivo da


convenção de nomes.

5.3 Obtendo um conhecimento mais profundo dos


programas Até agora neste capítulo, vimos que determinar os papéis das variáveis pode nos ajudar
razão sobre o código. No capítulo 4, apresentei outra técnica para ganhar rapidamente
conhecimento sobre código: circulando as variáveis e determinando os relacionamentos
entre eles. Essas técnicas são tremendamente úteis, mas são relativamente locais: elas
nos ajudar a entender partes individuais de código. Vamos agora nos concentrar em métodos para buscar
uma compreensão mais profunda do código. Qual era o objetivo de seu criador? O que eles eram
tentando alcançar, e que decisões foram tomadas nesse processo?

5.3.1 Conhecimento de texto versus conhecimento de plano

Dissecar diferentes níveis de compreensão era o objetivo de Nancy Pennington, professora de psicologia da
Universidade do Colorado. Ela criou um modelo de dois níveis diferentes nos quais um programador pode
entender o código-fonte: conhecimento da estrutura do texto
e planejar o conhecimento.
De acordo com o modelo de Pennington, o conhecimento da estrutura do texto relaciona-se ao nível da superfície.
compreensão de partes do programa, como saber o que uma palavra-chave faz ou
conhecer o papel de uma variável. O conhecimento do plano, por outro lado, representa a compreensão do
que um programador planejou quando criou o programa ou o que eles
pretendiam alcançar. Os objetivos do programador que criou o código não são
apenas ocultos em variáveis e seus papéis, mas tornam-se mais aparentes quando examinamos
como o código é estruturado e conectado. As próximas subseções irão ensiná-lo a cavar
mais profundamente nas intenções do código.
Machine Translated by Google

76 CAPÍTULO 5 Alcançando uma compreensão mais profunda do código

5.3.2 Diferentes estágios de compreensão do programa


Ter conhecimento do plano de um programa significa entender quais partes do código
relacionar com outras partes e como. O objetivo desta subseção é descrever a teoria
por trás da compreensão do código em detalhes, bem como sugerir exercícios para ajudá-lo a praticar ver
rapidamente o fluxo.
Jonathan Sillito, professor da Brigham Young University, definiu quatro estágios diferentes nos quais
uma pessoa pode entender o código.4 De acordo com Sillito, que observou
25 programadores enquanto estavam lendo o código, os programadores normalmente começam procurando
por um ponto focal no código. Este pode ser o ponto de entrada no código, como um
main() em um programa Java ou um método onLoad() em um aplicativo da web. Pode
também ser uma linha interessante por outro motivo, como uma linha na qual um erro foi
acabou de ocorrer ou uma linha que um criador de perfil sinalizou como consumindo muitos recursos.
A partir deste ponto focal, os programadores podem construir seu conhecimento. Isto pode ser feito
executando o código e colocando um ponto de interrupção nessa linha ou inspecionando o código,
por exemplo, pesquisando a base de código para outras ocorrências das variáveis
envolvidos ou usando recursos do IDE para pular para outros lugares no código usando essa linha.
A compreensão do programador cresce a partir daí, desenvolvendo-se em uma compreensão de um
conceito maior – por exemplo, entender os resultados de uma função
na entrada ou saber quais campos uma classe possui. Na fase final, o programador tem um
compreensão total de todo o programa, como ver que a linha focal de código é parte
de um determinado algoritmo ou entender todas as diferentes subclasses de uma classe.
Para resumir, os quatro passos comumente tomados ao passar de superficial
conhecimento de um programa para uma compreensão mais profunda são as seguintes:

1 Encontre um ponto focal.


2 Expanda o conhecimento a partir do ponto focal.
3 Compreender um conceito a partir de um conjunto de entidades relacionadas.

4 Compreender conceitos em várias entidades.

O ponto focal do código é uma noção importante ao ler o código. Simplificando, você
tem que saber por onde começar a ler. Algumas estruturas e técnicas, como estruturas de injeção de
dependência, podem fragmentar pontos focais para que fiquem distantes e
difícil de ligar. Para saber por onde começar, você precisa entender como
a estrutura vincula o código.
Tal situação pode deixar um leitor do código (e muitas vezes até mesmo o escritor)
não tem certeza da estrutura real do sistema em execução, mesmo que cada linha de código seja bastante
compreensível. Este é um exemplo de uma situação em que o programador tem texto
conhecimento, mas carece de conhecimento do plano. Isso pode ser frustrante porque você tem a sensação
que você deve saber o que o código faz (já que não parece complicado), mas o
estrutura subjacente é difícil de ver.

4
Consulte “Perguntas que os programadores fazem durante as tarefas de evolução do software” de Jonathan Sillito, Gail C. Murphy e
Kris De Volder (2006), www.cs.ubc.ca/~murphy/papers/other/asking-answering-fse06.pdf.
Machine Translated by Google

Adquirindo um conhecimento mais profundo dos programas 77

APLICAÇÃO DAS ETAPAS PARA ENTENDIMENTO PROFUNDO DO CÓDIGO

Agora que você entende a diferença entre conhecimento de plano e conhecimento de texto, vamos revisitar a técnica
demonstrada no capítulo 4 como uma forma de aliviar a capacidade cognitiva.
load ao ler código complexo:

1 Circule todas as variáveis.


2 Vincule variáveis semelhantes.

3 Circule todas as chamadas de método/função.


4 Vincule métodos/funções às suas definições.
5 Circule todas as instâncias de classes.
6 Desenhe um link entre as classes e suas instâncias.

Talvez você tenha percebido que esses seis passos são uma instanciação do modelo abstrato de Sillito.
A diferença é que nas etapas do capítulo 4 não havia um ponto de entrada específico; a
modelo foi aplicado a todas as variáveis, métodos e instâncias. Quando você quer ganhar um
compreensão mais profunda de uma parte específica do código, siga estas etapas, mas para um
ponto de entrada.

Essas etapas novamente são melhor executadas imprimindo o código em papel e destacando manualmente
partes dele. Como alternativa, você pode seguir as etapas em um IDE,
onde você adiciona comentários às linhas de código relevantes. Assim como fizemos com os seis passos
No capítulo 4, vamos percorrer o processo de quatro etapas para obter o conhecimento do código do plano com um
pouco mais de detalhes:

1 Encontre um ponto focal.


Comece sua exploração do código em um determinado ponto focal. Este pode ser o
main() , mas também pode ser uma certa parte do código que garante um
compreensão mais profunda, como a localização de um erro de tempo de execução ou uma linha de código
que um criador de perfil sinalizou como lento.

2 Expanda o conhecimento a partir do ponto focal.


Procure relacionamentos no código. Começando no ponto focal, circule todas as entidades relevantes
(variáveis, métodos e classes) que desempenham um papel. Você pode querer
para vincular variáveis semelhantes, por exemplo, acessos em uma lista, como clientes[0]
e clientes[i]. Expanda sua pesquisa observando a quais métodos e funções as linhas de código no primeiro
nível estão vinculadas.

O que você está destacando agora é chamado de fatia de código. A fatia de uma linha de
código X é definido como todas as linhas de código que se relacionam transitivamente com a linha X.

Concentrar-se em uma fatia ajuda a entender onde os dados são usados no programa.
Por exemplo, agora você pode se perguntar se existe uma determinada linha ou
método fortemente ligado ao ponto focal. Onde essas relações
ocorrer? Esses locais podem ser um ótimo ponto de partida para explorar o código em
mais profundidade. Quais partes do código são pesadas em chamadas de método? Esses também podem ser
bons pontos de foco para uma investigação mais aprofundada.
Machine Translated by Google

78 CAPÍTULO 5 Alcançando uma compreensão mais profunda do código

3 Compreender um conceito a partir de um conjunto de entidades relacionadas.


Agora você tem várias linhas destacadas relacionadas ao ponto focal. Há várias lições que podem
ser aprendidas com os padrões de chamada em um pedaço de código.
Por exemplo, existe um método chamado em vários lugares dentro da fatia que você destacou? Esse
método provavelmente desempenha um grande papel na base de código e merece uma investigação
mais aprofundada. Da mesma forma, quaisquer métodos que não estejam sendo usados no código
que você está estudando podem ser desconsiderados por enquanto. Quando você estiver editando o
código no IDE, talvez queira reformatar o código para que os métodos que estão sendo chamados
fiquem próximos ao ponto focal e os métodos que não estão em uso sejam colocados fora de sua exibição.
Isso economiza um pouco de carga cognitiva ao percorrer o código.
Também podemos ver quais partes dentro da fatia são pesadas em chamadas de método.
Partes de código fortemente conectadas provavelmente representam conceitos-chave, portanto,
também podem ser bons pontos de foco para um estudo mais aprofundado. Depois de investigar
melhor os locais importantes, você pode criar uma lista de todas as classes relacionadas. Escreva a
lista de relacionamentos e reflita sobre ela em profundidade. As entidades que você identificou e os
relacionamentos entre elas ajudam a formar uma ideia inicial do conceito por trás do código?

4 Compreender conceitos em várias entidades.


Como etapa final, você deseja obter uma compreensão de alto nível dos diferentes conceitos no
código. Por exemplo, você deseja entender não apenas as estruturas de dados contidas no código,
mas também as operações aplicadas a elas e suas restrições. O que você pode fazer e o que é
proibido?
Por exemplo, uma árvore é uma árvore binária ou um nó pode ter um número arbitrário de filhos?
Existem restrições na árvore? Por exemplo, um erro será gerado se você adicionar um terceiro nó ou
isso depende do usuário?

Na etapa final, você pode criar uma lista de conceitos presentes no código para documentar seu entendimento.
Tanto a lista de entidades resultante da etapa 3 quanto essa lista de conceitos podem ser valiosas para serem
adicionadas ao código como documentação.

EXERCÍCIO 5.2 Encontre outro pedaço de código desconhecido em sua própria base de código.
Como alternativa, você pode pesquisar algum código no GitHub. Realmente não importa qual
código você usa, mas deve ser algo com o qual você não esteja familiarizado.
Agora siga estas etapas para obter uma compreensão profunda desse código:

1 Encontre um ponto focal no código. Como você não está corrigindo um bug ou adicionando um recurso,
seu ponto de entrada no código provavelmente será o início do código — por exemplo, um método
main() .
2 Determine a fatia de código relacionada ao ponto focal, seja no papel ou no IDE.
Isso pode exigir alguma refatoração do código para aproximar o código envolvido
na fatia.
3 Com base em sua exploração na etapa 2, anote o que você aprendeu sobre o
código. Por exemplo, quais entidades e conceitos estão presentes no código e
como eles se relacionam entre si?
Machine Translated by Google

A leitura de texto é semelhante à leitura de código 79

5.4 Ler texto é semelhante a ler código Embora os


programadores tenham que ler muito código - como mencionado anteriormente neste livro,
estima-se que o programador médio gaste quase 60% de seu dia de trabalho lendo código em
vez de escrevê-lo5 - nós desenvolvedores não praticar muito a leitura de código. Para seu livro
Coders at Work (Apress, 2009), Peter Seibel entrevistou desenvolvedores sobre seus hábitos,
incluindo leitura de código. Enquanto a maioria das pessoas entrevistadas por Seibel disse que
ler código era importante e que os programadores deveriam fazê-lo mais, muito poucos deles
conseguiam nomear o código que tinham lido recentemente. Donald Knuth foi uma exceção
notável.
Como nos falta prática, boas estratégias e bons esquemas, muitas vezes devemos confiar
na praxis muito mais lenta de ler o código linha por linha ou percorrer o código com um
depurador. Isso, por sua vez, leva a uma situação em que as pessoas preferem escrever seu
próprio código em vez de reutilizar ou adaptar o código existente porque “é mais fácil construí-lo
sozinho”. E se fosse tão fácil ler código quanto ler linguagem natural? No restante deste capítulo,
primeiro exploraremos como a leitura de código e a linguagem de leitura são semelhantes e, em
seguida, mergulharemos nas técnicas de leitura de linguagem natural que podem ser aplicadas
à leitura de código para torná-la mais fácil.

5.4.1 O que acontece no cérebro quando lemos código?


Pesquisadores tentaram entender o que acontece no cérebro de uma pessoa quando ela está
programando há muito tempo. Vimos alguns exemplos iniciais disso no início do livro, como os
experimentos realizados na década de 1980 pela pesquisadora do Bell Labs Katherine
McKeithen, cujo trabalho abordamos no capítulo 2, onde ela pediu às pessoas que se
lembrassem dos programas ALGOL para formar um entendimento inicial de fragmentação em
programação.6 Os primeiros experimentos envolvendo programação e o cérebro costumavam
usar técnicas comuns na época, como fazer com que os participantes se lembrassem de
palavras ou palavras-chave. Embora esses métodos de pesquisa ainda sejam comumente
usados hoje, os pesquisadores também empregam técnicas mais modernas – e sem dúvida
muito mais frias. Estes incluem o uso de técnicas de imagem cerebral para obter uma
compreensão mais profunda de quais áreas do cérebro e processos cognitivos correspondentes,
acionam a programação.
ÁREAS DE BRODMANN

Embora ainda não se saiba muito sobre o cérebro, temos uma compreensão bastante decente
de quais partes do cérebro estão relacionadas a quais tipos de funções cognitivas.
Isso se deve principalmente ao neurologista alemão Korbinian Brodmann. Já em 1909, ele
publicou um livro, Vergleichende Lokalisationslehre der Großhirnrinde, detalhando as localizações
de 52 regiões diferentes do cérebro, agora conhecidas como áreas de Brodmann. Para cada área,

5
Consulte “Medindo a compreensão do programa: um estudo de campo em larga escala com profissionais” de Xin Xia et
al. (2017), https://ieeexplore.ieee.org/abstract/document/7997917.
6
“Organização do Conhecimento e Diferenças de Habilidades em Programadores de Computador” por Katherine B.
McKeithen et al. (1981), http://spider.sci.brooklyn.cuny.edu/~kopec/research/sdarticle11.pdf.
Machine Translated by Google

80 CAPÍTULO 5 Alcançando uma compreensão mais profunda do código

Brodmann detalhou as funções mentais que residem principalmente nessa região, como
lendo palavras ou lembrando. A quantidade de detalhes no mapa que ele produziu está aumentando
continuamente, graças a numerosos estudos nos anos seguintes.7
Por causa do trabalho de Brodmann e estudos subsequentes sobre as regiões do cérebro,
agora temos uma ideia razoável de onde as funções cognitivas “vivem” no cérebro humano.
Saber quais partes do cérebro estão associadas à leitura ou à memória de trabalho
nos ajudou a entender a essência de tarefas maiores.
Esses tipos de estudos podem ser feitos usando uma ressonância magnética funcional
(fMRI) . Uma máquina de fMRI pode detectar quais áreas de Brodmann estão ativas por
medir o fluxo sanguíneo no cérebro. Em estudos de fMRI, os participantes são comumente solicitados
para realizar uma tarefa complexa, como resolver um quebra-cabeça mental. Ao medir os aumentos de
fluxo sanguíneo para diferentes áreas de Brodmann, podemos determinar quais processos cognitivos
estão envolvidos na resolução dessa tarefa, como a memória de trabalho. Uma limitação da fMRI
máquina, no entanto, é que as pessoas não podem se mover enquanto a máquina está digitalizando. Como
tal, a gama de tarefas que os participantes podem realizar é limitada e não inclui
tarefas que envolvem fazer anotações ou produzir código.
EVIDÊNCIAS DA FMRI SOBRE O QUE O CÓDIGO FAZ NO CÉREBRO

A existência do mapa de Brodmann (e das máquinas fMRI, é claro) também deixou os cientistas curiosos
sobre programação. Que áreas cerebrais e funções cognitivas podem
estar envolvido? Em 2014, o primeiro estudo sobre programação em uma máquina de fMRI foi feito por
A professora alemã de ciência da computação Janet Siegmund.8 Os participantes foram convidados a ler
Código Java que representava algoritmos bem conhecidos, incluindo classificação ou pesquisa em um
lista e calcular a potência de dois números. Nomes de variáveis significativos no código
trechos foram substituídos por nomes ofuscados, para que os participantes gastassem
esforço para compreender o fluxo do programa em vez de adivinhar a funcionalidade do
o código baseado em nomes de variáveis.

As descobertas de Siegmund mostraram de forma confiável que a compreensão do programa ativa cinco
Áreas de Brodmann, todas localizadas no hemisfério esquerdo do cérebro: BA6, BA21, BA40,
BA44 e BA4.
O fato de as áreas de Brodmann BA6 e BA40 estarem envolvidas na programação não é
surpreendente. Essas áreas estão relacionadas à memória de trabalho (processador do cérebro) e
atenção. O envolvimento de BA21, BA44 e BA47, no entanto, pode ser um pouco mais surpreendente para
os programadores. Essas áreas estão relacionadas ao processamento de linguagem natural. Isto
encontrar é interessante porque Siegmund ofuscou todos os nomes de variáveis nos programas.
Isso sugere que, embora os nomes das variáveis estivessem ofuscados, os participantes estavam lendo
outros elementos do código (por exemplo, palavras-chave) e tentando extrair significado deles, assim como
fazemos ao ler palavras em um natural.
texto do idioma.

7
Se você estiver interessado, você pode visitar www.cognitiveatlas.org para um mapa recente.
8
Veja “Entendendo os cérebros dos programadores com fMRI” por Janet Siegmund et al. (2014), www.frontiersin
.org/10.3389/conf.fninf.2014.18.00040/event_abstract.
Machine Translated by Google

A leitura de texto é semelhante à leitura de código 81

5.4.2 Se você pode aprender francês, você pode aprender Python

Vimos que os exames de ressonância magnética mostraram que áreas do cérebro relacionadas à memória
de trabalho e ao processamento de linguagem estão envolvidas na programação. Isso implica que pessoas
com maior capacidade de memória de trabalho e melhores habilidades de linguagem natural serão melhores
programadores?
Pesquisas recentes lançam mais luz sobre a questão de quais habilidades cognitivas desempenham um
papel na programação. A professora associada Chantel Prat da Universidade de Washington liderou um
estudo sobre a conexão entre habilidades cognitivas e programação que avaliou o desempenho de seus
participantes (36 alunos que fizeram um curso de Python na Code Academy) em diversas áreas, incluindo
matemática, linguagem , e raciocínio, bem como capacidade de programação.9 Os testes que Prat usou para
medir as habilidades cognitivas de não programação dos participantes deste estudo eram comumente usados
e conhecidos por testar essas habilidades de forma confiável. Por exemplo, para habilidades matemáticas,
uma pergunta de exemplo diz: “Se cinco máquinas levarem 5 minutos para fazer cinco widgets, quanto tempo
levaria 100 máquinas para fazer 100 widgets?” O teste de raciocínio fluido assemelhava-se a um teste de QI;
os alunos, por exemplo, tinham que terminar uma sequência de imagens abstratas.

Para a capacidade de programação, os pesquisadores analisaram três fatores: as pontuações dos alunos
nos testes da Code Academy; um projeto final em que os alunos tiveram que criar um jogo de Pedra, Papel,
Tesoura; e um exame de múltipla escolha. Especialistas em Python criaram o exame e o esquema de notas
para o projeto final.
Como os pesquisadores tiveram acesso a pontuações de habilidades de programação e pontuações de
outras habilidades cognitivas para cada aluno, eles foram capazes de criar um modelo preditivo para ver quais
habilidades cognitivas previam a capacidade de programação. O que Prat e seus colegas descobriram pode
ser surpreendente para alguns programadores. A numeracia — o conhecimento e as habilidades que as
pessoas precisam para aplicar a matemática — teve apenas um efeito preditivo menor, prevendo apenas 2%
da variação entre os participantes. As habilidades de linguagem foram um melhor preditor, respondendo por
17% da variância. Isso é interessante porque nós, como campo, normalmente enfatizamos o fato de que as
habilidades matemáticas são importantes, e muitos programadores que conheço insistem que são ruins em
aprender linguagens naturais. O melhor preditor para todos os três testes foi a capacidade de memória de
trabalho e habilidades de raciocínio, respondendo por 34% da variação entre os participantes.

Neste estudo, os pesquisadores não apenas mediram as habilidades cognitivas dos 36 participantes,
mas também a atividade cerebral durante os testes, usando um dispositivo de eletroencefalografia (EEG). Ao
contrário de uma máquina de fMRI, este é um dispositivo relativamente simples que mede a atividade cerebral
com eletrodos colocados na cabeça. Os dados de EEG foram levados em consideração ao examinar as três
tarefas de programação.
Para a taxa de aprendizado – ou seja, a rapidez com que os alunos passaram pelo curso da Code
Academy – a habilidade no idioma foi um fator particularmente importante. A taxa de aprendizado e outras
habilidades de programação estavam correlacionadas, então não era como se os alunos rápidos fossem simplesmente

9
“Relacionando a Aptidão de Linguagem Natural às Diferenças Individuais na Aprendizagem de Linguagens de Programação” por Chantal S. Prat
et al. (2020), www.nature.com/articles/s41598-020-60661-8.
Machine Translated by Google

82 CAPÍTULO 5 Alcançando uma compreensão mais profunda do código

correndo pelo curso sem entender nada. Claro, o fator subjacente aqui pode ser que os alunos que leem
bem aprendem muito em geral, enquanto os alunos que lutam com a leitura não aprendem tão rápido ou
facilmente, independentemente do
domínio da programação que foram ensinados neste estudo.
Para a precisão da programação, medida pelo desempenho na tarefa Pedra, Papel e Tesoura, as
habilidades cognitivas gerais (incluindo memória de trabalho e raciocínio) eram as mais importantes. Para
conhecimento declarativo, medido por um teste de múltipla escolha, EEG
atividade também foi um fator importante. Conforme mostrado na figura 5.4, os resultados deste estudo
parecem indicar que o quão bem você pode aprender uma linguagem de programação é previsto por
sua habilidade em aprender linguagens naturais.

Variação Explicada pelo Tipo de Preditor e


Resultado de Aprendizagem
100
90
80
70
60
50
40
30
20
10
0
Programação Declarativo Preditivo médio
Taxa de Aprendizagem
precisão conhecimento Utilitário

Cognição geral aptidão


NeuropsicometriaIdioma
Aritmética Variação inexplicada

Figura 5.4 Os resultados do estudo de Prat, que mostram que as habilidades numéricas (em azul claro) são apenas um
preditor menor da capacidade de programação. A aptidão da linguagem (em rosa) é um preditor muito mais forte,
especialmente da rapidez com que se pode aprender uma linguagem de programação. Fonte: www.nature.com/articles/
s41598-020-60661-8.pdf . Fonte: Chantal S. Prat et al. (2020).

Este é um resultado que pode ser um tanto inesperado para muitos programadores. Computador
a ciência é muitas vezes vista como um STEM (ciência, tecnologia, engenharia e matemática)
campo e agrupados com essas disciplinas em universidades (incluindo a minha). Na cultura
de programação, as habilidades matemáticas são procuradas como habilidades úteis ou mesmo necessárias.
Essas novas descobertas podem exigir que atualizemos nosso pensamento sobre o que prevê a capacidade
de programação.

COMO AS PESSOAS LÊEM CÓDIGOS?

Antes de mergulharmos na leitura do código, vamos refletir sobre como lemos um texto (não-ficção),
como um jornal. O que você faz quando lê um artigo de jornal?
Machine Translated by Google

A leitura de texto é semelhante à leitura de código 83

Existem muitas estratégias que as pessoas costumam usar ao ler o texto. Por exemplo,
você pode escanear o texto antes de lê-lo em profundidade para determinar se vale a pena seu tempo.
Você também pode olhar conscientemente para as imagens que aparecem ao lado do texto enquanto lê
para ajudar a apoiar sua compreensão do texto e seu contexto ou fazer anotações para resumir o que você está lendo
ou para destacar as partes mais importantes. Digitalizando texto
e olhar para as imagens que acompanham são estratégias de compreensão do texto. Muitos desses
as estratégias são ensinadas e praticadas ativamente na escola, então é provável que elas sejam automáticas e você
as use sem pensar.
Vamos nos aprofundar em suas habilidades de leitura de código em breve, mas primeiro vamos ver o que
pesquisas científicas descobriram sobre como as pessoas lêem código.

EXERCÍCIO 5.3 Pense em uma ocasião em que você estava lendo um texto de não ficção. O que
estratégias você empregou antes, durante e depois da leitura do texto?

QUANDO OS PROGRAMADORES LÊEM O CÓDIGO, ELES VERIFICAM PRIMEIRO

Quando os pesquisadores querem entender o que as pessoas olham, eles usam rastreadores oculares. Olho
rastreadores são dispositivos que podem ser usados para determinar onde em uma tela ou página as pessoas
estão focando sua atenção. Eles são usados extensivamente em pesquisas de marketing para determinar quais tipos
de anúncios chamam a atenção das pessoas por mais tempo. Os rastreadores oculares podem ser
dispositivos físicos e eram usados já na década de 1920, quando ainda ocupavam toda uma
sala. Os rastreadores oculares modernos são muito menores. Eles podem trabalhar com hardware, como um Mic
rosoft Kinect rastreamento de profundidade, ou até mesmo ser totalmente baseado em software e rastrear o usuário
olhar com reconhecimento de imagem.
Os rastreadores oculares permitiram que os pesquisadores entendessem melhor como as pessoas leem o código.
Por exemplo, uma equipe de pesquisadores do Instituto de Ciência e Tecnologia de Nara,
liderado pelo professor Hidetake Uwano, observou que os programadores escaneiam o código para obter um
ideia do que o programa faz.10 Eles descobriram que nos primeiros 30% do tempo gasto
revisando o código, os participantes visualizaram mais de 70% das linhas. Fazendo isso
tipo de varredura rápida é um comportamento comum ao ler a linguagem natural para obter uma visão geral da
estrutura do texto, e parece que as pessoas transferem essa estratégia para
código de leitura.

INICIANTES E ESPECIALISTAS LÊEM O CÓDIGO DE FORMA DIFERENTE

Para comparar como os desenvolvedores leem código e como as pessoas leem linguagem natural, Teresa
Busjahn, pesquisadora da Freie Universität Berlin, liderou um estudo envolvendo 14 programadores novatos e 6
especialistas.11 Busjahn e seus colegas estudaram pela primeira vez a diferença
entre a leitura de texto e a leitura em linguagem natural. Ela descobriu que o código é lido menos
linearmente do que a linguagem natural: programadores iniciantes seguiram um caminho linear no texto
com aproximadamente 80% de seus movimentos oculares, enquanto eles lêem linearmente em 75% dos

10
Consulte “Analisando o desempenho individual da revisão do código-fonte usando o movimento dos olhos dos revisores” de Hidetake
Uwano et ai. (2006), www.cs.kent.edu/~jmaletic/cs69995-PC/papers/Uwano06.pdf.
11
Ver “Eye Movements in Code Reading: Relaxing the Linear Order” de Teresa Busjahn et al. (2015), https://
ieeexplore.ieee.org/document/7181454.
Machine Translated by Google

84 CAPÍTULO 5 Alcançando uma compreensão mais profunda do código

movimentos oculares para o código. Nos casos em que os programadores novatos não liam lin
antes, eles geralmente seguiam a pilha de chamadas em vez de ler de cima para baixo.
Busjahn não apenas comparou código a texto, ela também comparou programadores novatos
com programadores experientes. A comparação das práticas de leitura de código dos novatos com
as dos especialistas revelou uma diferença nas práticas de leitura de código dos dois grupos: os
novatos leem mais linearmente e seguem a pilha de chamadas com mais frequência do que os
programadores especialistas. Aprender a seguir a pilha de chamadas enquanto lê o código,
aparentemente, é uma prática que vem com a experiência.

5.5 Estratégias de compreensão de texto aplicadas ao código Como


mostrado na seção anterior, as habilidades cognitivas utilizadas para a leitura de código são
semelhantes às utilizadas para a leitura de linguagem natural. Isso significa que podemos aplicar
os insights obtidos ao estudar como as pessoas leem textos em linguagem natural à leitura de código.
Tem havido muita pesquisa sobre estratégias de leitura eficazes e como aprendê-las. As
estratégias para compreensão de leitura podem ser divididas aproximadamente nestas sete
categorias:12 ÿ Ativação – Pensar ativamente em coisas relacionadas para ativar o conhecimento

prévio ÿ Monitoramento – Acompanhar sua compreensão de um texto ÿ Determinar a importância


– Decidir quais partes de um texto são mais relevantes ÿ Inferir – Preencher fatos que não são
dados explicitamente no texto ÿ Visualizar – Desenhar diagramas do texto lido para aprofundar
a compreensão ÿ Questionar – Fazer perguntas sobre o texto em questão ÿ Resumir – Criar um
breve resumo de um texto

Como há semelhanças cognitivas entre a leitura de código e a leitura de texto, é possível que as
estratégias de leitura de linguagem natural também sejam úteis na leitura de código. Esta seção
explora cada uma das sete estratégias conhecidas para leitura de texto no contexto de leitura de
código.

5.5.1 Ativando o conhecimento prévio


Sabemos que os programadores escaneiam um novo código antes de mergulhar nele. Mas por que
escanear o código seria útil? Uma razão é que ele lhe dará uma noção inicial dos conceitos e
elementos sintáticos que estão presentes no código.
Nos capítulos anteriores, vimos que quando você pensa sobre as coisas, sua memória de
trabalho pesquisará em seu LTM por memórias relacionadas. Pensar ativamente nos elementos do
código ajudará sua memória de trabalho a encontrar informações relevantes armazenadas no LTM
que podem ser úteis para compreender o código em mãos. Uma boa estratégia para ativar
deliberadamente o conhecimento prévio é reservar um tempo fixo – digamos, 10 minutos – para
estudar código e ter uma noção do que se trata.

12 “Os sete hábitos dos leitores altamente eficazes”, por Kathy Ann Mills, 2008, https://www.researchgate.net/
publication/27474121_The_Seven_Habits_of_Highly_Effective_Readers .
Machine Translated by Google

Estratégias de compreensão de texto aplicadas ao código 85

EXERCÍCIO 5.4 Estude um trecho de código desconhecido por um período fixo de tempo
(digamos, 5 ou 10 minutos, dependendo da duração). Depois de estudar o código por
esse período fixo de tempo, tente responder às seguintes perguntas concretas sobre ele:

ÿ Qual foi o primeiro elemento (variável, classe, conceito de programação e assim por diante)
que chamou sua
atenção? ÿ Por que isso?
ÿ Qual foi a segunda coisa que você notou? ÿ
Por quê? ÿ Essas duas coisas (variáveis,
classes, conceitos de programação) estão relacionadas? ÿ Quais conceitos estão
presentes no código? Você conhece todos eles? ÿ Quais elementos sintáticos estão
presentes no código? Você conhece todos eles? ÿ Quais conceitos de domínio estão
presentes no código? Você conhece todos eles?

O resultado deste exercício pode motivá-lo a procurar mais informações sobre


programação desconhecida ou conceitos de domínio no código. Quando você encontra
um conceito desconhecido, é melhor tentar estudá-lo antes de mergulhar no código
novamente. Aprender sobre um novo conceito ao mesmo tempo que ler um novo
código provavelmente causará uma carga cognitiva excessiva, tornando o conceito e o
código menos eficazes.

5.5.2 Monitoramento
Ao ler o código, é importante acompanhar o que você está lendo e se você o entende. Mantenha
uma nota mental não apenas do que você entende, mas também do que você acha confuso.
Uma boa estratégia é imprimir o código e marcar as linhas que você entende e as que te
confundem. Você pode fazer isso com ícones como os que você usou para marcar os papéis das
variáveis.
A Figura 5.5 mostra uma parte do código JavaScript que anotei dessa maneira — uso uma
marca de verificação para o código que entendo e um ponto de interrogação para linhas ou
partes de linhas que me confundem. Monitorar sua compreensão dessa forma o ajudará quando
você ler o código pela segunda vez, pois poderá se concentrar nas linhas confusas com mais
profundidade.
Anotações marcando o que o confunde podem ser muito úteis para monitorar sua própria
compreensão, mas também podem ajudar na busca de ajuda. Se você puder comunicar
claramente quais partes do código são confusas, você pode pedir ao autor uma explicação sobre
certas linhas. Isso será mais eficaz do que se você disser: “Não tenho ideia do que isso faz”.
Machine Translated by Google

86 CAPÍTULO 5 Alcançando uma compreensão mais profunda do código

Figura 5.5 Um trecho


de código em JavaScript
anotado com ícones que
indicar compreensão. Uma
marca de verificação significa
que a linha foi compreendida
e um ponto de interrogação
indica que uma determinada linha é confusa.

5.5.3 Determinando a importância de diferentes linhas de código


Ao ler o código, pode ser muito útil refletir sobre quais linhas são importantes. Você
pode fazer isso como um exercício deliberado. Não importa tanto quantas linhas importantes você escolha
- podem ser 10 para um pequeno trecho ou 25 para um programa maior.
O que importa é que você pense sobre quais partes do código provavelmente terão o
maior influência na execução do programa.
Se estiver usando código impresso, você pode marcar linhas importantes com uma exclamação
marca.

EXERCÍCIO 5.5 Selecione um pedaço de código desconhecido e dê a si mesmo alguns minutos


para decidir sobre as linhas mais importantes do programa. Uma vez que você tenha
selecionou as linhas, responda a estas perguntas:

ÿ Por que você selecionou essas linhas?


ÿ Qual é o papel dessas linhas? Por exemplo, são linhas que executam
ização, ou entrada/saída, ou processamento de dados?
ÿ Como essas linhas estão conectadas ao objetivo geral do programa?

O QUE É UMA LINHA DE CÓDIGO IMPORTANTE? Você pode se perguntar o que é importante
linha de código é, e essa é uma ótima pergunta! Muitas vezes fiz o exercício de
marcando importantes linhas de código com uma equipe de desenvolvimento, onde cada equipe
membro marca individualmente o que eles acham que são as linhas mais importantes e
a equipe compara as notas depois.
Machine Translated by Google

Estratégias de compreensão de texto aplicadas ao código 87

Não é incomum que as pessoas em uma equipe discordem sobre quais linhas são
importante. Algumas pessoas argumentam que as linhas onde os cálculos mais intensivos
acontecem são as linhas mais importantes, enquanto outras pessoas selecionam uma
import para uma biblioteca relevante ou um comentário explicativo. Pessoas
com experiência em diferentes linguagens de programação ou domínios podem
têm ideias diferentes sobre a importância de determinadas linhas de código e
isso está ok. Não pense nisso como um desacordo a ser resolvido, mas uma oportunidade
aprender.

O bom de fazer este exercício com sua equipe é que não apenas
ensiná-lo sobre o código, mas também sobre você e seus colegas de equipe (prioridades,
experiência e assim por diante).

5.5.4 Inferindo o significado dos nomes das variáveis


Muito do significado de um programa está contido na estrutura do próprio código – por
exemplo, no uso de loops e instruções condicionais. Há também significado no
nomes de elementos de programa, como variáveis, e alguns desses significados podem precisar
ser inferida. Se o código contiver uma variável chamada embarque, pode ser valioso ganhar
uma compreensão do que uma remessa significa dentro do domínio do código. Um envio é o mesmo
que um pedido, um grupo de produtos destinado a um cliente? Ou um embarque é um conjunto de
produtos a serem enviados para uma fábrica?
Como você já viu, nomes de variáveis podem servir como sinalizadores importantes: dicas sobre
do que se trata o código. Ao ler o código, pode ser valioso pagar conscientemente
atenção a eles.

Um exercício para isso é percorrer o código linha por linha e criar uma lista de todos os
nomes de identificadores (variáveis, classes, métodos, funções). Isso é algo que você pode fazer
mesmo quando você está totalmente confuso sobre o que o código faz. Pode parecer um pouco estranho
analisar o código tão mecanicamente, mas ler todos os identificadores ajudará seu trabalho
memória. Depois de se concentrar nos nomes, você pode pesquisar em seu LTM por
em formação. A informação encontrada suporta a memória de trabalho para processar
o código com mais facilidade.

Depois de criar a lista de identificadores, você pode usá-la para obter uma
compreensão do código. Por exemplo, você pode dividir os nomes das variáveis em dois
diferentes categorias: nomes de variáveis que estão relacionadas ao domínio do código, como
como Cliente ou Pacote, e nomes de variáveis relacionados à programação
conceitos, como Árvore ou Lista. Alguns nomes de variáveis combinam ambas as categorias, como
CustomerList ou FactorySet. Outros não podem ser entendidos sem contexto, o que
significa que você precisará gastar mais esforço para investigar seu significado - por exemplo,
tentando decidir qual papel eles desempenham usando a estrutura de Sajaniemi, discutida anteriormente
neste capítulo.

EXERCÍCIO 5.6 Selecione uma parte do código-fonte e crie meticulosamente uma lista de
todos os nomes de variáveis presentes no programa.
Machine Translated by Google

88 CAPÍTULO 5 Alcançando uma compreensão mais profunda do código

Preencha a tabela a seguir para todos os nomes de variáveis.

O nome é compreensível sem


Nome Domínio? Conceito?
olhar para o código?

Usando a tabela de nomes de variáveis, você pode responder a estas perguntas:

ÿ Qual é o domínio ou tópico do código?


ÿ Quais conceitos de programação são usados?
ÿ O que você pode aprender com esses nomes?
ÿ Quais nomes estão relacionados entre si?

ÿ Existem nomes que são ambíguos quando vistos sem contexto? ÿ Que significado
podem ter nomes ambíguos nesta base de código?

5.5.5 Visualizando
Nos capítulos anteriores, você viu várias técnicas para visualizar código para obter uma visão mais profunda
compreensão, incluindo a criação de uma tabela de estados e o rastreamento do fluxo de um pedaço de código.
Existem várias outras estratégias de visualização que podem ser usadas para entender
código. Uma técnica que pode ser útil para códigos muito complexos, dos quais uma análise mais profunda
compreensão é necessária é listar todas as operações nas quais as variáveis estão envolvidas.

TABELAS DE OPERAÇÃO

Ao trabalhar com código desconhecido, às vezes pode ser difícil prever como o
os valores das variáveis mudarão à medida que o código for executado. Quando o código é muito difícil de
processar imediatamente, pode ser útil criar uma tabela de operações. Por exemplo, o seguinte
O trecho de código JavaScript pode ser difícil de entender se você não souber que uma função zip mescla duas
listas.

Listagem de código JavaScript 5.1 que compacta listas como e bs usando uma determinada função f

zipWith: function (f, as, bs) {


var comprimento = Math.min(as.comprimento, bs.comprimento);
var z = [];
for (var i = 0; i < comprimento; i++) {
zs[i] = f(as[i], bs[i]);
}
retornar z;
}

Nesse caso, pode ajudar a inspecionar as variáveis, métodos e funções e determinar em quais operações eles
estão envolvidos. Por exemplo, f é aplicado a as[i] e
Machine Translated by Google

Estratégias de compreensão de texto aplicadas ao código 89

bs[i], então é uma função. Inspecionando as e bs, vemos que eles estão sendo
indexados, então essas variáveis devem ser listas ou dicionários. Depois de determinar
os tipos de variáveis em um código complexo por meio de suas operações, pode ser
mais fácil determinar suas funções.

EXERCÍCIO 5.7 Selecione um pedaço de código desconhecido e anote os nomes de todas


as variáveis, funções e classes no código. Em seguida, liste todas as operações associadas
a cada identificador.

Nome do identificador Operações)

Depois de criar esta tabela, leia o código novamente. O preenchimento da tabela ajudou você a
obter uma compreensão mais profunda dos papéis das variáveis e do significado do problema
como um todo?

5.5.6 Questionamento
Fazer perguntas a si mesmo enquanto lê o código o ajudará a entender os objetivos e a funcionalidade do
código. Nas seções anteriores, você viu vários exemplos de perguntas que você pode fazer sobre código.
Algumas perguntas mais valiosas são as seguintes:

ÿ Quais são os cinco conceitos mais centrais do código? Podem ser nomes de identificadores,
temas, classes ou informações encontradas nos comentários. ÿ Que estratégias você utilizou
para identificar os conceitos centrais? Por exemplo, você olhou para nomes de métodos,
documentação ou nomes de variáveis ou se baseou em seu conhecimento prévio de um
sistema?
ÿ Quais são os cinco conceitos de ciência da computação mais centrais no código? Podem ser
algoritmos, estruturas de dados, suposições ou técnicas. ÿ O que você pode determinar sobre
as decisões tomadas pelo(s) criador(es) do código – por exemplo, a decisão de implementar uma
determinada versão de um algoritmo, usar um determinado padrão de projeto ou usar uma
determinada biblioteca ou API? ÿ Em que suposições essas decisões se baseiam? ÿ Quais
são os benefícios dessas decisões?

ÿ Quais são algumas desvantagens potenciais dessas decisões? ÿ


Você consegue pensar em soluções alternativas?

Essas perguntas são mais profundas do que o conhecimento da estrutura do texto e podem ajudá-lo a chegar
a um plano para entender o código.
Machine Translated by Google

90 CAPÍTULO 5 Alcançando uma compreensão mais profunda do código

5.5.7 Código de resumo


Uma estratégia final de compreensão de texto que podemos aplicar à compreensão de código
é resumir o que você acabou de ler. Escrever um resumo do código em linguagem natural
ajudará você a obter uma compreensão mais profunda do que está acontecendo nesse código.
Este resumo também pode servir como documentação adicional, seja para você pessoalmente
ou até mesmo como documentação real do código, se estiver faltando anteriormente.
Algumas das técnicas que abordamos anteriormente neste capítulo podem ser de grande
ajuda para resumir o código. Por exemplo, olhar para as linhas mais importantes, listar todas
as variáveis e suas operações relacionadas e refletir sobre as decisões que foram tomadas
pelo criador do código são ótimas maneiras de começar um resumo.

EXERCÍCIO 5.8 Resuma um trecho de código preenchendo a tabela a seguir. Claro, você
pode adicionar mais informações ao resumo do que sugeri neste exercício.

Origem

Objetivo do código: O que o código está tentando alcançar?

Linhas de código mais importantes

Conceitos de domínio mais relevantes

Construções de programação mais relevantes

Decisões tomadas na criação do código

Resumo ÿ
Ao ler um código desconhecido, descobrir quais funções as variáveis desempenham, como
stepper ou valor mais procurado, pode ajudar a aprofundar sua compreensão. ÿ Quando
se trata de entender o código, existe uma diferença entre o conhecimento da estrutura do
texto, que significa conhecer os conceitos sintáticos usados no código, e o conhecimento
do plano, que significa entender as intenções do criador do código. ÿ Existem muitas
semelhanças entre ler código e ler linguagem natural, e sua capacidade de aprender uma
linguagem natural pode ser um preditor de sua capacidade de aprender a programar.

ÿ Estratégias que são comumente usadas para auxiliar na compreensão profunda de textos em
linguagem natural, como visualização e resumo, também podem ser aplicadas para obter uma
compreensão mais profunda do código.
Machine Translated by Google

Melhorar na resolução
problemas de programação

Este capítulo abrange


ÿ Aplicando modelos para raciocinar sobre programação
problemas de forma mais eficaz
ÿ Descobrir que diferentes formas de pensar sobre os
problemas podem influenciar a forma como os resolvemos
ÿ Explorar o uso de modelos para pensar em código e resolver
problemas de forma mais eficaz
ÿ Examinar técnicas para aprender novas maneiras de resolver
problemas melhorando o LTM
ÿ Praticar técnicas para usar modelos para resolver
problemas apoiando a memória de trabalho
ÿ Analisar corretamente os problemas de escopo, abstraindo
detalhes irrelevantes e incluindo os importantes

Nos capítulos anteriores, você aprendeu sobre diferentes processos cognitivos ativos em
o cérebro ao programar. Exploramos como as informações são armazenadas brevemente no
STM durante a leitura do código e como as informações são recuperadas do LTM quando ele
precisa ser aplicado. Também discutimos a memória de trabalho, que está ativa quando

91
Machine Translated by Google

92 CAPÍTULO 6 Melhorando na resolução de problemas de programação

pensamos em código. Então, no capítulo 5, discutimos estratégias para envolver profundamente


com código desconhecido.

O foco deste capítulo é como resolvemos problemas. Como programador profissional,


muitas vezes você pesará soluções diferentes para os problemas. Você vai modelar todos os clientes
de uma empresa como uma lista simples ou como uma árvore, organizada por sua filial padrão? Você poderia
usar uma arquitetura baseada em microsserviços ou toda a lógica deve estar em um só lugar?
Quando você está considerando diferentes soluções para problemas, muitas vezes você descobrirá que
diferentes alternativas têm valor. Decidir qual solução usar pode ser difícil porque
há tantos fatores a ter em conta. Por exemplo, você priorizará a facilidade de
uso ou desempenho? Você considerará as possíveis alterações que espera no código em
o futuro ou você vai apenas olhar para a tarefa atual?
Este capítulo apresenta duas estruturas que podem ajudá-lo a obter mais informações sobre
como tomar decisões sobre diferentes projetos de software. Primeiro estudaremos a mente
representações que o cérebro cria enquanto resolve problemas e programa. Sendo
ciente das representações que você usa para pensar sobre o código permitirá que você resolva mais
tipos de problemas e raciocinar sobre código e resolver problemas de forma mais eficaz. Isto
capítulo cobre duas técnicas envolvendo modelos que irão ajudá-lo a fortalecer sua
LTM e apoie sua memória de trabalho para atingir esses objetivos.
Em segundo lugar, veremos como pensamos sobre computadores ao resolver problemas.
Ao programar, nem sempre consideramos todos os aspectos das máquinas em que estamos trabalhando. Às
vezes podemos abstrair muitos dos detalhes - por exemplo, quando você está criando uma interface de
usuário, a maioria das especificidades do sistema operacional não são tão
relevante. No entanto, ao implementar um modelo de aprendizado de máquina ou ao criar um
phone app, as especificações da máquina que o código irá rodar em questão. O
A segunda estrutura que veremos neste capítulo ajuda você a pensar sobre problemas
no nível certo de abstração.

6.1 Usando modelos para pensar sobre código


Quando as pessoas resolvem problemas, quase sempre criam modelos. Os modelos são simplificados
representações da realidade, e o objetivo principal de um modelo é apoiá-lo no pensamento
sobre um problema e, finalmente, resolvê-lo. Os modelos podem ter várias formas e níveis
de formalidade. Um cálculo aproximado na parte de trás de uma esteira de cerveja é um modelo, mas um diagrama
de relacionamento de entidade de um sistema de software também é um modelo.

6.1.1 Os benefícios do uso de modelos


Nos capítulos anteriores, criamos vários tipos de modelos que apoiavam o pensamento
sobre o código. Por exemplo, criamos um diagrama de estados para mostrar os valores das variáveis, como
mostrado na figura 6.1. Também criamos um gráfico de dependência, que é outro tipo de
modelo de código.

Usar modelos explícitos de código ao resolver um problema tem dois benefícios. Primeiro, os modelos
podem ajudar a comunicar informações sobre programas a outras pessoas. Se eu criei um
diagrama de estado, posso mostrar a outra pessoa todos os valores intermediários das variáveis para
Machine Translated by Google

Usando modelos para pensar sobre código 93

N N2 B$ N1
1 LET N2 = ABS (INT (N))
2 LET B$ =
"" Iniciar 77 7
3 PARA N1 = N2 PARA 0 PASSO 0 Ciclo 1 3 31
4 LET N2 = INT (N1 / 2)
5
Ciclo 2
LET B$ = STR$ (N1 - N2 * 2) + B$
6 DEIXE N1 = N2
7 PRÓXIMO N1
8 IMPRIMIR B$
9 DEVOLUÇÃO

Figura 6.1 Um programa BASIC que converte o número N em uma representação binária. Você pode usar
um auxílio de memória como a tabela de estado parcial mostrada aqui para ajudá-lo a entender como o programa funciona.

ajudá-los a entender como o código funciona. Isso é especialmente útil com sistemas maiores. Por exemplo, se
olharmos juntos para um diagrama de arquitetura de código, posso apontar
classes e os relacionamentos entre elas e objetos que de outra forma seriam
invisível ou oculto no código.

Um segundo benefício dos modelos é que eles podem ajudá-lo a resolver problemas. Quando você é
perto do limite de itens que você pode processar de uma só vez, fazer um modelo pode ser uma maneira útil
para diminuir a carga cognitiva. Assim como uma criança pode somar 3+5 usando uma reta numérica (uma espécie de
modelo) em vez de fazer o cálculo em sua cabeça, os programadores podem mapear
a arquitetura de um sistema em um quadro branco porque é muito difícil manter todos os elementos de uma grande
base de código na memória de trabalho.
Os modelos podem ser uma grande ajuda na resolução de problemas porque ajudam o LTM a identificar
memórias relevantes. Muitas vezes, os modelos têm restrições; por exemplo, apenas um diagrama de estado
mostra os valores das variáveis e um diagrama entidade-relacionamento mostra apenas classes e
seus relacionamentos. Concentrar-se em uma determinada parte de um problema pode apoiar seu pensamento
sobre uma solução, e essas restrições forçam você a fazer exatamente isso. Pensar na linha numérica ao somar
ajuda a colocar o foco na atividade de contar, e uma entidade
diagrama de relacionamento força você a pensar sobre quais entidades ou classes seu sistema
consistem e como se relacionam entre si.

NEM TODOS OS MODELOS SÃO IGUALMENTE ÚTEIS

Nem todos os modelos que podemos usar para pensar sobre problemas são iguais, no entanto. Como programadores,
sabemos a importância da representação e seus efeitos na resolução de problemas. Por exemplo, dividir um número
por dois é trivial quando convertemos o
número para uma representação binária - nós simplesmente precisamos deslocar os bits para a direita por um.
Embora este seja um exemplo relativamente simples, existem vários problemas em que a representação influencia a
estratégia de solução.
A importância da representação pode ser bem ilustrada com mais profundidade pelo seguinte problema relativo
a um pássaro e dois trens. Um pássaro está sentado em um trem prestes a
deixar Cambridge, Inglaterra, em direção a Londres. Assim que esse trem parte, um segundo trem
parte de Londres, a 50 milhas de distância. O pássaro decola e voa em direção ao segundo
Machine Translated by Google

94 CAPÍTULO 6 Melhorando na resolução de problemas de programação

trem a uma velocidade de 75 milhas por hora. Ambos os trens estão viajando a 50 milhas por hora.
Quando o pássaro chega ao segundo trem, ele se vira e voa de volta para o primeiro, e continua
fazendo isso até que os dois trens se encontrem. A que distância o pássaro voou quando os trens
se encontraram?
A primeira intuição de muitas pessoas é pensar nos trens e como o pássaro se move
entre eles (figura 6.2).

Figura 6.2 Modelando a distância que um pássaro percorre entre dois trens em movimento, a
partir da visão do pássaro. Isso leva a uma solução correta, mas muito complexa, envolvendo
o cálculo das localizações de ambos os trens.

Modelar a trajetória do pássaro é uma solução correta, mas envolve um conjunto complicado de
equações que a maioria das pessoas preferiria evitar. Uma solução mais fácil se concentra no
próprio pássaro e é o seguinte. Os trens se encontrarão no meio entre Londres e Cambridge após
30 minutos. Nesse ponto, ambos os trens viajaram 25 milhas.
Como o pássaro voa a uma velocidade de 75 milhas por hora, depois de meia hora, o pássaro
voou 37,5 milhas. Esta é uma boa ilustração do fato de que a maneira como você pensa sobre um
problema pode afetar fortemente a maneira como você o resolve e o esforço necessário para fazê-lo.
Na programação, também trabalhamos com diferentes representações de problemas. Algumas
linguagens de programação limitam o número de representações possíveis, o que pode ser útil e
prejudicial na resolução de problemas. Por exemplo, uma linguagem como APL é perfeita para
modelar soluções que envolvem matrizes, mas pode ser difícil de usar para problemas que
precisam de uma representação diferente. Java, por outro lado, tem a opção de criar classes para
representar todos os tipos de problemas, então você pode usar Java para problemas envolvendo
matrizes, mas precisará fazer o trabalho extra de criar uma classe de matrizes. Em Java, é mais
provável que você busque uma solução envolvendo dois loops for aninhados, porque eles são
incorporados e comumente usados.

6.2 Modelos mentais


Até agora trabalhamos com modelos que são explicitamente criados fora de nossos cérebros.
Tabelas de estado, gráficos de dependência e diagramas de relacionamento de entidade são
modelos construídos em papel ou quadro branco. Você pode optar por fazer esse modelo quando
precisar se comunicar com outras pessoas ou pensar mais profundamente sobre um problema. Mas lá
Machine Translated by Google

Modelos mentais 95

também são modelos que você pode usar ao pensar em um problema que não é feito
explícito fora do seu cérebro. São os chamados modelos mentais.
Na seção anterior você viu que a representação que você usa para resolver um problema
influencia como você pensa sobre o problema. O mesmo vale para os modelos mentais:
alguns apoiam o pensamento melhor do que outros. Nesta seção, exploraremos o que mental
modelos são e como você pode usá-los explicitamente ao resolver problemas.
Um exemplo de modelo mental que usamos para código é pensar em atravessar uma árvore.
No código e no computador, é claro, não há uma árvore real que atravessamos; lá
são apenas valores na memória que pensamos como uma estrutura de árvore. Este modelo nos ajuda a
raciocinar sobre o código. É mais fácil pensar nos “filhos desse nó” do que
pense sobre “os elementos aos quais esse elemento se refere”.

Os modelos mentais foram inicialmente descritos pelo filósofo escocês Kenneth Craik em
seu livro de 1943 The Nature of Explanation. Craik descreveu os modelos mentais como
“modelos em escala” de fenômenos na natureza. De acordo com Craik, as pessoas usam modelos mentais
para prever, raciocinar e explicar o mundo ao seu redor.
A definição de modelo mental que eu mais gosto é esta: um modelo mental criado

uma abstração em sua memória de trabalho que você pode usar para raciocinar sobre o
problema em mãos.
Ao interagir com computadores, criamos muitos tipos de modelos mentais. Por
Por exemplo, ao pensar em um sistema de arquivos, você pode pensar em um grupo de arquivos em uma pasta
juntos. Claro, quando você pensa sobre isso um pouco mais profundamente, você sabe que há
não são realmente arquivos ou pastas em seu disco rígido; um disco rígido contém apenas zeros e
uns. Mas quando pensamos nesses zeros e uns, pensamos neles como sendo organizados nessas estruturas.

Também usamos modelos mentais quando pensamos em código. Um exemplo de mente


modelo que usamos quando pensamos em programação é a ideia de que uma linha específica de
código está sendo executado. Mesmo para linguagens compiladas é assim que raciocinamos sobre
programas, enquanto é claro que o que é executado não é a linha de Java ou C em si, mas a
bytecode gerado correspondente a essa linha. Mesmo que a execução de linhas de
código não é uma representação correta ou completa de como funciona a execução do programa, ele
pode ser um modelo útil para usar ao raciocinar sobre programas.
Os modelos também podem falhar conosco - por exemplo, quando você percorre um código altamente
otimizado em um depurador e descobre que o otimizador do compilador transformou o
código abaixo o suficiente para que o depurador se mova de maneiras que parecem não relacionadas
como você acha que o código-fonte será executado.

EXERCÍCIO 6.1 Considere um trecho de código que você usou nos últimos dias. O que
modelos mentais desse código que você usou durante a programação? Será que aqueles
modelos mentais dizem respeito ao computador ou execução de código ou outros aspectos da
programação?
Machine Translated by Google

96 CAPÍTULO 6 Melhorando na resolução de problemas de programação

6.2.1 Examinando modelos mentais em detalhes


Os modelos mentais compartilham uma característica importante com os modelos expressos fora do
cérebro: o modelo representa adequadamente o problema, mas é mais simples e abstrato
do que a realidade. Os modelos mentais também possuem outras características importantes, que estão
destacadas na tabela 6.1.

Tabela 6.1 Principais características dos modelos mentais, combinados com exemplos em programação

Característica Exemplo

Os modelos mentais são incompletos. Um modelo mental não Pensar em uma variável como uma caixa que contém um valor
precisa ser um modelo completo do sistema-alvo, assim como um não suporta adequadamente o pensamento sobre reatribuição. O
modelo em escala simplifica o objeto físico que modela de segundo valor caberá na caixa com o primeiro valor? Ou o primeiro
algumas maneiras. Um modelo mental incompleto pode ser útil valor será empurrado para fora?
para seu titular se abstrair os detalhes irrelevantes.

Os modelos mentais são instáveis. Os modelos mentais não Quando aprendemos a programar, é útil pensar em uma variável
precisam permanecer os mesmos para sempre; na verdade, como uma caixa que contém um valor. No entanto, depois de um
eles muitas vezes mudam durante o período em que estão em tempo, percebemos que uma variável não pode conter mais de um
uso. Por exemplo, se você criar um modelo mental de eletricidade valor, então uma etiqueta de nome se torna uma analogia melhor.
como o fluxo de água, poderá imaginá-lo inicialmente como um rio
reto, mas como um rio que se alarga e se estreita quando aprende
mais sobre como a eletricidade flui. Os detentores de um modelo
mental também podem esquecer partes do modelo quando não
se envolvem suficientemente com ele.

Vários modelos mentais podem coexistir, mesmo que sejam Você pode pensar em uma variável como uma caixa contendo
inconsistentes entre si. Os novatos em particular muitas vezes um valor. Alternativamente, você pode pensar em uma variável como
têm modelos mentais “localmente coerentes, mas globalmente uma tag de nome que você anexa a um valor.
inconsistentes”, que tendem a estar intimamente ligados aos Ambos os modelos mentais podem existir ao mesmo tempo e podem
detalhes do caso particular que estão considerando.
ter benefícios em diferentes situações.

Modelos mentais podem ser “estranhos” e até mesmo parecer Você já pediu a um computador para fazer algo, como “Por favor,
superstições. Muitas vezes as pessoas acreditam em coisas que trabalhe desta vez?” Mesmo sabendo que um computador não é um
realmente não fazem sentido. ser senciente e não pode ouvi-lo, você ainda pode ter um modelo
mental de um computador como uma entidade que pode decidir agir
a seu favor.

As pessoas são frugais ao usar modelos mentais. Por exemplo, ao depurar, muitos programadores preferem fazer
Como o cérebro consome muita energia, as pessoas normalmente pequenas alterações em seu código (ajustes) e executá-lo novamente
preferem fazer um trabalho físico extra se isso economizar esforço para ver se o bug foi corrigido, em vez de gastar energia para criar
mental. um bom modelo mental do problema.

a Gentner, Dedre. (2002). Psicologia dos modelos mentais. Em NJ Smelser & PB Bates (Eds.), Enciclopédia
Internacional das Ciências Sociais e Comportamentais (pp. 9683-9687), Elsevier.
Machine Translated by Google

Modelos mentais 97

6.2.2 Aprendendo novos modelos mentais


Como é mostrado na tabela 6.1, as pessoas podem ter modelos mentais diferentes e concorrentes em suas
mentes ao mesmo tempo. Você pode pensar superficialmente em um arquivo como estando “em” uma pasta,
sabendo também que, na verdade, um arquivo é uma referência a um local no disco rígido
onde as informações são armazenadas.

Ao aprender a programar, as pessoas geralmente aprendem novos modelos mentais gradualmente. Por
Por exemplo, inicialmente você pode pensar em um documento no disco rígido como uma folha de papel
física real com palavras armazenadas em algum lugar, enquanto mais tarde você aprende que
o disco rígido só pode armazenar zeros e uns. Ou você pode primeiro pensar em uma variável e
seu valor como nome e número de telefone em um catálogo de endereços, atualize-o
modelo quando você aprender mais sobre como funciona a memória do computador. Você pode
pense que quando você aprende como algo funciona com mais profundidade, o velho modelo mental
“errado” é removido de seu cérebro e substituído por um melhor. No entanto, em capítulos anteriores vimos
que não é provável que essa informação desapareça
completamente do LTM. Isso significa que há sempre o risco de você recorrer
modelos mentais incorretos ou incompletos que você aprendeu anteriormente. Múltiplos modelos mentais
podem permanecer ativos simultaneamente, e os limites entre os modelos não são
sempre claro. Então, especialmente em uma situação de alta carga cognitiva, você pode de repente
usar um modelo antigo.

Como exemplo de modelos mentais concorrentes, considere este enigma: o que acontece com
um boneco de neve se você o vestir com um suéter bonito e quente? Será que vai derreter mais rápido ou mais lento do que
seria sem um suéter?

Seu primeiro pensamento pode ser que o boneco de neve derreterá mais rápido, já que seu cérebro
imediatamente busca o modelo mental de um suéter sendo uma coisa que fornece
cordialidade. Mas após uma análise mais aprofundada, você concluirá que um suéter não
dá calor, mas ajuda a manter o calor do nosso corpo. Porque o suéter isola,
ele manterá o frio que o boneco de neve perde, então o boneco de neve realmente derreterá
mais lento, não mais rápido.

Da mesma forma, você pode recorrer a modelos mentais simples ao ler


código. Por exemplo, ao ler um código que usa muito ponteiros, você pode confundir
valores e endereços de memória, misturando modelos mentais de variáveis e ponteiros. Ou,
ao depurar código complexo usando chamadas assíncronas, você pode usar um
modelo mental antigo e incompleto de código síncrono.

EXERCÍCIO 6.2 Pense em dois modelos mentais que você conhece para um único conceito de
programação, como variáveis, loops, armazenamento de arquivos ou gerenciamento de memória.
Quais são as semelhanças e diferenças entre as duas mentes?
modelos?

6.2.3 Como usar modelos mentais de forma eficiente ao pensar em código


Nos capítulos anteriores, discutimos diferentes processos cognitivos no cérebro. Nós
falou sobre o LTM, onde não apenas memórias sobre eventos da vida, mas também resumos
Machine Translated by Google

98 CAPÍTULO 6 Melhorando na resolução de problemas de programação

representações de conhecimento, chamadas de esquemas, são armazenadas. Também cobrimos o


memória de trabalho, que é onde o pensamento acontece.
Você pode se perguntar a quais modelos mentais de processos cognitivos estão associados. São
esses modelos armazenados no LTM e recuperados quando necessário? Ou funciona
forma de memória ao pensar em código? Entendendo como os modelos mentais
são processados é importante porque esse conhecimento pode nos ajudar a melhorar nosso uso de
esses modelos. Se eles estiverem localizados principalmente no LTM, podemos nos treinar para lembrá-los com
flashcards, enquanto se forem criados na memória de trabalho, podemos
deseja usar visualizações para apoiar esse processo cognitivo em seu uso de modelos mentais.
Curiosamente, depois do livro inicial de Kenneth Craik sobre modelos mentais, o tópico
não foi estudado mais por quase 40 anos. Então, em 1983, dois livros, ambos chamados Modelos Mentais,
foram publicados por diferentes autores. Os autores desses livros tinham visões diferentes sobre como os
modelos mentais são processados no cérebro, como você verá na
seções seguintes.

MODELOS MENTAIS NA MEMÓRIA DE TRABALHO

O primeiro livro sobre modelos mentais que saiu em 1983 foi de Philip Johnson-Laird, um
professor de psicologia em Princeton. Johnson-Laird argumentou que os modelos mentais são
usados enquanto raciocinam e, portanto, vivem na memória de trabalho. Em seu livro, ele descreve uma
estudo onde ele e um colega investigaram o uso desses modelos. Os participantes foram
conjuntos de sentenças que descrevem uma mesa: por exemplo, “A colher está para o
direita do garfo” e “O prato está à direita da faca”. Após ouvir as descrições, os participantes foram solicitados a
realizar algumas tarefas não relacionadas. Em seguida, foram dadas
quatro descrições de arranjos de mesa e perguntou qual das descrições era mais semelhante à que havia
recebido.
Das descrições que os participantes puderam escolher, duas foram completamente
falso, um era o verdadeiro que eles tinham ouvido, e um era uma descrição que poderia ser
inferido do layout. Por exemplo, de “A faca está à esquerda do garfo” e
“O garfo está à esquerda do prato” pode-se inferir que o prato está à direita da faca
sem ser dito explicitamente. Os participantes foram então solicitados a classificar as quatro descrições,
começando com aquele que melhor representa a descrição que eles receberam.
Eles geralmente classificavam tanto as descrições reais quanto as inferidas mais altas do que as descrições.
dois falsos, dos quais os pesquisadores concluíram que os participantes fizeram uma
modelo em suas mentes da configuração da mesa, que eles usaram para selecionar a resposta certa.
O que podemos aprender com o trabalho de Johnson-Laird para melhorar o pensamento sobre programação
é que ter um modelo abstrato de código é útil: ele permite que você raciocine sobre
o modelo em si, em vez de confiar na referência ao código, o que seria menos
eficiente.

MODELOS DE CONCRETO FUNCIONAM MELHOR

Mais adiante neste capítulo, exploraremos como criar deliberadamente modelos mentais quando
raciocínio sobre o código. Mas primeiro precisamos discutir um aspecto do estudo de Johnson-Laird
ainda não cobrimos. Seu experimento teve uma reviravolta interessante!
Machine Translated by Google

Modelos mentais 99

Os participantes receberam diferentes tipos de descrições. Em alguns casos, o original


descrições que receberam correspondiam a apenas uma configuração de tabela. No entanto, às vezes o
descrições originais podem corresponder a diferentes arranjos realistas. Por exemplo, em
figura 6.3 as afirmações “O garfo está à esquerda da colher” e “A colher está
à direita do garfo” se encaixam nas duas configurações da mesa. Por outro lado, a afirmação “O
prato está entre a colher e o garfo” só se encaixa na configuração da mesa esquerda na figura 6.3.

Figura 6.3 Exemplo de configurações de tabela que os participantes foram solicitados


a combinar com as descrições fornecidas. Aqui, “O garfo está à esquerda da colher”
se encaixa nas duas configurações da mesa.

Quando Johnson-Laird comparou o desempenho dos participantes em ambos os tipos de


descrições - determinado e indeterminado - ele descobriu que aqueles que haviam sido
determinadas descrições selecionaram as respostas corretas com mais frequência do que aquelas
que receberam descrições que poderiam corresponder a várias configurações de tabela. A diferença foi
grande: 88% de acertos para o primeiro grupo versus 58% para o segundo.
Esses resultados indicam que a criação de um modelo mais concreto de uma situação apoia fortemente o
raciocínio.
Trazer esses resultados de volta à programação indica que quanto mais detalhes um modelo mental
tiver, mais fácil será raciocinar sobre o sistema em questão e responder a perguntas sobre o sistema
corretamente.

CRIANDO MODELOS MENTAIS DE CÓDIGO-FONTE NA MEMÓRIA DE TRABALHO


Vimos neste capítulo que os modelos mentais, quando corretos e concretos,
pode apoiar o pensamento sobre sistemas complexos. Isso leva à questão de como criar tais modelos
mentais. Quando o código é simples, você pode criar um
modelo sem muito esforço. No entanto, quando o código é mais complexo ou você tem menos
conhecimento sobre a base de código ou o domínio, a criação de um modelo mental preciso
dê mais trabalho. Vale a pena, porém, porque esse modelo pode ser um grande trunfo.
Seguir essas etapas pode ajudá-lo a formar um modelo mental de código complexo em seu
memória de trabalho:

1 Comece criando modelos locais.


Nos capítulos anteriores, você aprendeu sobre como usar modelos desenhados à mão para
sua memória de trabalho, como criar tabelas de estado e gráficos de dependência.
Embora esses métodos sejam locais, representando apenas uma pequena parte de uma base de código, eles
também pode ajudá-lo a criar um modelo mental de um pedaço maior de código, de duas maneiras.
Primeiro, ao apoiar a memória de trabalho, esses modelos locais reduzem
Machine Translated by Google

100 CAPÍTULO 6 Melhorando na resolução de problemas de programação

carga, para que você possa se concentrar na criação de um modelo mental maior com mais facilidade.
Em segundo lugar, esses modelos menores podem ser os blocos de construção de um modelo mental
maior. Por exemplo, um gráfico de dependência pode apontar para algumas linhas de código fortemente
conectadas que podem desempenhar um papel importante na formação de um modelo mental.

2 Liste todos os objetos relevantes na base de código e os relacionamentos entre os objetos.


Ao formar um modelo mental de código, você quer ser capaz de raciocinar sobre os elementos
nele. Um modelo mental de um programa que cria faturas, por exemplo, pode incluir a restrição de que
uma pessoa pode ter várias faturas, mas uma fatura pertence apenas a uma pessoa. Para entender
as interações entre os diferentes elementos no código, primeiro liste os elementos usando um quadro
branco ou uma ferramenta digital, depois mapeie os relacionamentos. Isso ajudará a fornecer uma
imagem mais clara do sistema geral.

3 Responda a perguntas sobre o sistema e use as respostas para refinar seu modelo.
Agora você pode tentar responder a perguntas sobre o sistema com o qual está trabalhando usando
o modelo mental que construiu nas etapas 1 e 2 e verificá-las no código. Quais são as perguntas certas
depende do sistema em questão, mas algumas perguntas genéricas que geralmente funcionam são
as seguintes: a Quais são os elementos mais importantes (classes, objetos, páginas) do sistema? Eles
estão presentes no modelo?

b Quais são as relações entre esses elementos importantes? c Quais são


os principais objetivos do programa? d Como a meta se relaciona com os
elementos centrais e seus relacionamentos? e O que é um caso de uso típico? Ela
é coberta no modelo?
MODELOS MENTAIS EM LTM
Até agora, nesta seção, falamos sobre o uso de modelos mentais para raciocínio, que Johnson-Laird descreveu
como situados na memória de trabalho. No entanto, há uma visão diferente sobre os modelos mentais, que os
descreve como armazenados no LTM.
O segundo livro sobre modelos mentais que apareceu em 1983 foi escrito por Dedre Gentner e Albert
Stevens, pesquisadores da empresa de P&D Bolt, Beranek e Newman Inc. (BBN). Ao contrário de Johnson-
Laird, eles argumentaram que os modelos mentais genéricos são armazenados no LTM e podem ser
recuperados quando necessário.

Por exemplo, uma pessoa pode armazenar um modelo mental de como os líquidos fluem, que é usado ao
despejar leite em um copo. Como o modelo é genérico, as pessoas saberão que, ao despejar a massa de
panqueca em uma tigela, o líquido pode se comportar um pouco diferente do leite, porque é mais espesso,
mas ainda se encaixa no modelo mental de como o líquido flui.
Como isso se aplica à programação? Na programação, você pode armazenar uma representação abstrata
de como a travessia de árvore funciona: há uma raiz na qual você inicia e, em seguida, você pode explorar a
largura primeiro, observando todos os filhos de um determinado nó, ou a profundidade primeiro, onde você
escolha um filho e explore seus filhos até que você não possa prosseguir. Quando você encontra um programa
que trabalha com árvores, pode invocar o modelo mental genérico de árvores.
Machine Translated by Google

Modelos mentais 101

A descrição de modelos mentais de Gentner e Stevens é um pouco semelhante ao sche


mata no LTM: os modelos mentais armazenados no LTM ajudam a organizar os dados e podem
ser usados quando você encontrar novas situações semelhantes às que já viu antes. Por exemplo,
você provavelmente pode entender a travessia de árvore em uma linguagem de programação
que você nunca viu antes usando seu modelo mental armazenado anteriormente.

CRIANDO MODELOS MENTAIS DE CÓDIGO-FONTE NO LTM


Pensar em modelos mentais dessa maneira levaria a uma maneira diferente de melhorar seu uso
de modelos mentais. Em vez de criar instâncias concretas de modelos mentais ao ler código-fonte
complexo, a visão de Gentner e Steven indicaria que para fazer melhor uso deles você precisa
construir um vocabulário maior de modelos mentais potenciais. Nos capítulos anteriores, você
também aprendeu sobre formas de expandir as informações armazenadas no LTM.

Uma maneira que abordamos de expandir o conhecimento armazenado em seu LTM é o uso
de flashcards. Os flashcards que discutimos no capítulo 3 tinham um conceito de programação
de um lado e o código correspondente do outro. Se você deseja armazenar mais modelos mentais
para usar ao raciocinar sobre código, também pode usar flashcards. No entanto, eles conterão
informações diferentes agora. O objetivo desta segunda forma de flashcards não é ampliar seu
conhecimento de conceitos sintáticos, mas ampliar seu vocabulário de modelos mentais ou
maneiras de pensar sobre código. Coloque o nome de um modelo mental em um lado do cartão
(o prompt) e uma breve explicação ou visualização do modelo mental do outro lado.

Quais modelos mentais você usará para pensar sobre código depende em parte do domínio,
da linguagem de programação e da arquitetura do código. No entanto, algumas coisas geralmente
valem a pena usar:
ÿ Estruturas de dados, como gráficos direcionados e não direcionados e diferentes formas de
listas ÿ Padrões de projeto, como o padrão observador ÿ Padrões arquitetônicos, como Model–
View–Controller ÿ Diagramas, como diagramas de relacionamento de entidade ou diagramas de
sequência ÿ Ferramentas de modelagem, como diagramas de estado ou redes de Petri

Há duas maneiras de usar um conjunto de flashcards de modelos mentais. Você pode usá-los
para testar seus conhecimentos, da mesma forma que os cartões de sintaxe: leia um prompt e
verifique se você conhece a explicação correspondente. Conforme explicado anteriormente,
sempre que você encontrar um padrão com o qual não está familiarizado, poderá adicionar uma
carta ao seu baralho de modelos mentais.
Uma segunda maneira de usar o baralho é ao ler o código com o qual você está lutando.
Percorra seu baralho de modelos mentais e decida para cada um se ele pode se aplicar ao código
em questão.
Por exemplo, se você escolher um cartão sobre árvores, pergunte a si mesmo: “Posso pensar
nesse código na forma de uma árvore?” Se esse padrão se aplicar, você pode começar a criar
um modelo inicial baseado neste cartão. Para uma árvore, isso significa decidir quais nós, folhas
e arestas estariam presentes no modelo e o que eles podem representar.
Machine Translated by Google

102 CAPÍTULO 6 Melhorando na resolução de problemas de programação

EXERCÍCIO 6.3 Crie um baralho inicial de flashcards com modelos mentais que

pode ser útil para o código em que você está trabalhando. Escreva o nome do mental
modelo de um lado como um prompt e uma explicação do modelo do outro
lateral. Anexe a explicação com perguntas a serem feitas ao aplicar este
modelo. Por exemplo, para uma árvore, você modelará nós, folhas e arestas, de modo que um
A pergunta inicial poderia ser “Que pedaços de código podem ser representados como folhas?”
Da mesma forma, para usar o modelo mental de uma tabela de estados, você precisará criar uma lista
de variáveis, então uma pergunta inicial poderia ser "Quais são as variáveis?"

Você também pode fazer este exercício juntos como uma equipe e aprender uns com os outros
modelos. Ter um vocabulário compartilhado de modelos mentais pode facilitar muito a comunicação
sobre código.

MODELOS MENTAIS VIVEM NO LTM E NA MEMÓRIA DE TRABALHO


Ambas as visões de modelos mentais - que eles são usados na memória de trabalho e que
eles são armazenados em LTM—ainda são comumente mantidos. Enquanto as duas opiniões concorrentes
sobre modelos mentais pode parecer contraditório, como vimos neste capítulo, ambas as teorias têm seu valor
e, de fato, elas se complementam muito bem. Na década de 1990
estudos mostraram que ambos são verdadeiros até certo ponto: modelos mentais armazenados em LTM

pode influenciar a construção de modelos mentais na memória de trabalho.1

6.3 Máquinas nocionais


Na seção anterior discutimos modelos mentais, representações que formamos em
nossos cérebros ao raciocinar sobre problemas. Os modelos mentais são genéricos e encontrados em
todos os domínios. No entanto, a pesquisa em linguagens de programação também usa o conceito de um
máquina nocional. Enquanto um modelo mental pode ser um modelo de qualquer coisa no mundo em
Em geral, uma máquina nocional é um modelo que usamos quando raciocinamos sobre como um computador
executa o código. Para ser mais preciso, uma máquina nocional é uma representação abstrata
do computador que usamos para pensar sobre o que ele está fazendo.
Ao tentar entender como um programa ou linguagem de programação funciona,
na maioria das vezes não estamos interessados em todos os detalhes sobre como o computador físico funciona.
Não nos importamos como os bits são armazenados usando eletricidade. Em vez disso, nos importamos
sobre o efeito da linguagem de programação em um nível conceitual mais alto, como
trocando dois valores ou encontrando o maior elemento em uma lista. Para indicar a diferença
entre a máquina física real e o que a máquina faz em um nível mais abstrato.
nível, usamos o termo “máquina nocional”.
Por exemplo, uma máquina fictícia para Java ou Python pode incluir o conceito de
referências, mas pode omitir endereços de memória. Os endereços de memória podem ser considerados um
detalhes de implementação que você não precisa estar ciente ao programar em Java ou
Pitão.

1
Para uma visão geral, veja “Toward a Unified Theory of Reasoning” de Johnson-Laird e Khemlani, https://www.
.sciencedirect.com/science/article/pii/B9780124071872000010.
Machine Translated by Google

Máquinas fictícias 103

Uma máquina nocional é uma abstração consistente e correta da execução de um

linguagem de programação, ainda que incompleta, como acabamos de ver. Máquinas nocionais,
portanto, diferem dos modelos mentais de um programador, o que pode estar errado
ou inconsistente.
A maneira mais clara que encontrei para entender a diferença entre uma máquina nocional e um
modelo mental é que uma máquina nocional é uma explicação de como um computador funciona.
Quando você internalizou a máquina nocional e pode usá-la com facilidade, a máquina nocional se
torna seu modelo mental. Quanto mais você aprende sobre uma linguagem de programação, mais
próximo seu modelo mental fica da máquina nocional.

6.3.1 O que é uma máquina nocional?

O termo é um pouco enigmático, portanto, antes de examinarmos exemplos de máquinas nocionais


e como as usamos na programação, quero descompactar o termo em si. O primeiro ponto a ser
destacado é que uma máquina nocional representa uma máquina, algo com o qual se pode interagir
à vontade. Essa é uma diferença importante dos modelos mentais que formamos sobre, por exemplo,
física ou química. Embora certamente possamos usar experimentos científicos para construir uma
compreensão do mundo ao nosso redor, também há muitas coisas com as quais não podemos
experimentar, ou pelo menos não com segurança. Por exemplo, quando estamos formando modelos
mentais do comportamento dos elétrons ou da radioatividade, é difícil ter uma configuração
experimental em casa ou no trabalho com a qual possamos aprender com segurança. Na
programação, temos uma máquina com a qual podemos interagir a qualquer momento. Máquinas
nocionais são projetadas para construir um entendimento correto de uma máquina que executa código.
A outra parte do termo é nocional, que de acordo com o dicionário Oxford significa “existir como
ou baseado em uma sugestão, estimativa ou teoria; não existe na realidade”. Quando estamos
considerando como um computador faz o que faz, não estamos interessados em todos os detalhes.
Estamos principalmente interessados em uma versão hipotética e idealizada de como um computador
funciona. Por exemplo, quando pensamos em uma variável x obtendo um valor 12, na maioria dos
casos não nos importamos com o endereço de memória onde o valor está armazenado e o ponteiro
que conecta x a esse endereço de memória. Basta pensar em x como uma entidade com valor atual
que mora em algum lugar. Uma máquina nocional é a abstração que usamos para raciocinar sobre o
funcionamento do computador no nível de abstração necessário em um determinado momento.

6.3.2 Exemplos de máquinas fictícias

A ideia da máquina nocional foi concebida por Ben du Boulay, professor da Universidade de Sussex,
enquanto trabalhava no Logo na década de 1970. Logo era uma linguagem de programação
educacional projetada por Seymour Papert e Cynthia Solomon. Foi a primeira linguagem a introduzir
a tartaruga: uma entidade que pode desenhar linhas e ser controlada por código. O nome vem da
palavra grega logos, que significa palavra ou pensamento.
Machine Translated by Google

104 CAPÍTULO 6 Melhorando na resolução de problemas de programação

du Boulay usou pela primeira vez o termo “máquina nocional” para descrever sua estratégia para ensinar
Logo a crianças e professores. Ele o descreveu como “o modelo idealizado do computador implícito nas
construções da linguagem de programação”. du Boulay's
as explicações incluíam visualizações feitas à mão, mas usavam principalmente analogias.
Por exemplo, du Boulay usou um operário de fábrica como analogia para o modelo de execução da
linguagem. O trabalhador é capaz de executar comandos e funções, e
tem ouvidos com os quais pode ouvir valores de parâmetros, uma boca que fala saídas e
mãos que realizam as ações descritas pelo código. Esta representação de conceitos de programação começou
simples, mas foi gradualmente construída para explicar a totalidade de
a linguagem Logo, incluindo comandos internos, procedimentos e funções definidos pelo usuário, chamadas de
subprocedimentos e recursão.
Como você viu, máquinas fictícias são projetadas para explicar o funcionamento de um
máquina executando código e, portanto, eles também compartilham algumas características com
máquinas. Por exemplo, como uma máquina física, uma máquina nocional tem a noção de
"Estado." Quando pensamos em uma variável como uma caixa, essa caixa virtual e nocional pode estar vazia ou
tem um valor “dentro” dele.
Existem também outras formas de máquinas fictícias que não estão tão ligadas ao hardware.

Você provavelmente usa representações abstratas da máquina que é executada quando você lê
e escrever código. Por exemplo, quando pensamos em como os cálculos funcionam em linguagens de
programação, muitas vezes comparamos o funcionamento do computador ao de um matemático. Considere
esta expressão em Java como um exemplo:

duplo celsius = 10;


duplo fahrenheit = (9,0/5,0) * celsius + 32;

Quando você tem que prever o valor de fahrenheit, você provavelmente usará uma forma de substituição: você
substitui a variável celsius na segunda linha por seu valor, 10 .
passo, você pode adicionar mentalmente colchetes para indicar a precedência do operador:

duplo fahrenheit = ((9,0/5,0) * 10) + 32;

Realizar um cálculo mental baseado na transformação é um modelo perfeito do que


será calculado, mas não representa o cálculo que uma máquina realiza.
O que acontece no computador real é bem diferente, é claro. A máquina mais
provavelmente usa uma pilha para avaliação. Ele transformará a expressão em polonês reverso
notação e empurre o resultado de 9.0/5.0 para a pilha, antes de retirá-lo para multiplicá-lo por 10 e empurrar os
resultados de volta para cálculos adicionais. Este é um perfeito
exemplo de uma máquina fictícia parcialmente errada, mas útil. Poderíamos chamar isso de
“máquina nocional de substituição”, e provavelmente está mais próximo do modelo mental da maioria
programadores do que o modelo baseado em pilha.
Machine Translated by Google

Máquinas fictícias 105

6.3.3 Diferentes níveis de máquinas nocionais

Você já viu alguns exemplos de máquinas fictícias agora. Alguns operam no nível da linguagem de
programação e abstraem todos os detalhes da máquina subjacente, como a máquina nocional de
substituição.
Outras máquinas nocionais, como representar uma pilha como uma pilha física de papéis, são
mais representativas de como a máquina física executa o programa. Ao usar máquinas nocionais
como uma forma de explicar e entender os conceitos de programação, pode ser útil pensar
propositalmente em quais detalhes a máquina nocional esconde e expõe. A Figura 6.4 mostra uma
visão geral de quatro níveis diferentes de abstração nos quais uma máquina nocional
pode operar, incluindo um exemplo por nível. Por exemplo, “variáveis como caixas” desempenha um
papel no nível da linguagem de programação e no nível do compilador/intérprete, mas abstrai detalhes
sobre o código compilado e o sistema operacional.

Programação Cálculo como


Língua substituição

Compilador/ Variáveis como


interpretador caixas

Máquina virtual/ Operações mutuamente exclusivas


bytecode como interruptores de trem

Sistema Threads como


operacional colaboração humana

Figura 6.4 Diferentes níveis em que uma máquina nocional pode usar abstrações.
Por exemplo, a máquina nocional “cálculo como substituição” que vimos abstrai tudo além da
linguagem de programação, enquanto representa threads como a colaboração humana se
concentra no funcionamento do sistema operacional.

Pode ser importante perceber quais detalhes você está ignorando ao raciocinar sobre o código.
Embora abstrair detalhes seja uma ótima maneira de obter uma compreensão de código de alto nível,
algumas formas de pensar sobre código podem abstrair detalhes relevantes.

EXERCÍCIO 6.4 Liste três exemplos de máquinas fictícias e os níveis em que operam.
Preencha a tabela a seguir listando máquinas nocionais e selecionando seu nível de
abstração.

Nocional Linguagem de Compilador/ Máquina virtual/ Sistema


máquina programação interpretador bytecode operacional
Machine Translated by Google

106 CAPÍTULO 6 Melhorando na resolução de problemas de programação

6.4 Máquinas nocionais e linguagem


Frequentemente usamos máquinas fictícias não apenas para raciocinar sobre como as máquinas funcionam, mas também
para falar sobre código. Dizemos, por exemplo, que uma variável “contém” um valor, mesmo que de
é claro que não há um objeto físico que tenha um valor numérico armazenado nele. Esta linguagem indica um
modelo mental de uma variável que é como uma caixa contendo um valor.
Existem muitos exemplos de linguagem sobre programação que sugerem
máquinas nocionais e levam a certos modelos mentais. Por exemplo, dizemos que um arquivo
é "aberto" ou "fechado", onde tecnicamente significa que temos permissão para ler o arquivo ou que somos
solicitados a fazê-lo. Usamos a palavra “ponteiro” e comumente dizemos que um ponteiro “aponta”
a um determinado valor, e dizemos que uma função “retorna” um valor quando coloca o valor
na pilha para que o chamador (também um modelo mental) possa usá-lo.
Máquinas fictícias que são comumente usadas para explicar como as coisas funcionam encontram seu
caminho para a linguagem que usamos para falar sobre código, e até mesmo para as próprias linguagens de
programação. Por exemplo, o conceito de ponteiro está presente em muitas linguagens de programação, e
muitos IDEs permitem que você veja onde uma determinada função é “chamada”.

EXERCÍCIO 6.5 Liste mais três exemplos de linguagem que usamos sobre programação
que indicam o uso de uma certa máquina nocional e levam a uma certa
modelo mental.

6.4.1 Expandindo conjuntos de máquinas nocionais


Anteriormente, usei o termo “máquina nocional” como se houvesse apenas uma máquina nocional em
jogar a qualquer momento. Na realidade, as linguagens de programação não têm necessariamente
apenas uma máquina nocional abrangente, mas sim um conjunto de máquinas sobrepostas. Por
Por exemplo, ao aprender sobre tipos simples, você pode ter sido informado de que uma variável
é como uma caixa contendo um valor. Mais tarde em sua carreira de programação, você aprendeu sobre
tipos compostos, que podem ser pensados como pilhas de caixas, cada uma contendo um
valor. Essas duas máquinas fictícias são construídas em cada

outro, como mostra a figura 6.5.


Existem vários outros exemplos de expansão
5
conjuntos de abstrações que usamos para entender conceitos de
linguagem de programação. Por exemplo, considere a
10
máquina fictícia usada para pensar na passagem de parâmetros
em uma linguagem que suporte funções. Inicialmente, você pode
pense em uma função sem parâmetros como um pacote X 10 012 8
para várias linhas de código. Quando os parâmetros de entrada são
adicionados e passamos de um procedimento para uma função, Figura 6.5 Duas máquinas
a função pode ser vista como um viajante que embala um conjunto nocionais que podem ser compostas.
À esquerda está a visualização de um
de valores em sua mochila e traz os valores para o
variável como uma caixa, e à direita
localização da chamada no código. Quando os parâmetros de saída são uma matriz é mostrada como uma
também considerado, o viajante também traz de volta um valor em pilha de caixas.

sua mochila, conforme ilustrado na figura 6.6.


Machine Translated by Google

Máquinas nocionais e linguagem 107

def print_square(x): def quadrado(x):


5
5
print(x * x) retornar x * x

25

print_square(5) print(quadrado(5))

Figura 6.6 Duas máquinas nocionais para funções. À esquerda está uma máquina que suporta apenas parâmetros de
entrada e à direita está um modelo estendido que também incorpora parâmetros de saída.

6.4.2 Diferentes máquinas nocionais podem criar modelos mentais conflitantes


Na seção anterior você viu que algumas máquinas nocionais podem ser compostas, como a
máquina nocional de uma variável como uma caixa, que compõe com a máquina nocional de um
array como uma pilha de caixas. No entanto, máquinas nocionais também podem criar modelos
mentais que entram em conflito entre si.
Por exemplo, a máquina nocional que descreve uma variável como uma caixa difere daquela
em que uma variável é imaginada como uma etiqueta de nome. As duas máquinas nocionais não
podem ser combinadas em um modelo mental consistente; ou pensamos em uma variável como
uma ou outra. Cada uma dessas máquinas nocionais tem vantagens e desvantagens. Por exemplo,
a variável como uma representação de caixa implica que uma variável pode conter vários valores,
como uma caixa pode conter várias moedas ou doces. Essa maneira (defeituosa) de pensar é
menos provável se você pensar em uma variável como uma etiqueta de nome ou um adesivo. Um
adesivo só pode ser colocado em uma coisa, então uma variável só pode ser usada para descrever
um valor. Em 2017, meu grupo de pesquisa realizou um estudo no NEMO Science Museum em
Amsterdã, onde exploramos esse conceito.2 Os 496 participantes, que não tinham nenhuma
experiência de programação antes do estudo, receberam uma aula introdutória de programação
no Scratch. Scratch é uma linguagem de programação baseada em blocos desenvolvida pelo MIT
para ser acolhedora e acessível a crianças que estão aprendendo a programar. Embora seja
destinado a iniciantes que estão dando seus primeiros passos na programação, ele suporta
recursos mais avançados, incluindo definição e uso de variáveis. A criação de uma variável é feita
pressionando um botão e inserindo o nome da variável, enquanto a configuração de uma variável
é feita com o bloco de programação representado na figura 6.7.

Em nosso estudo, introduzimos a todos os


participantes o conceito de variável, mas não da
mesma forma. Metade dos participantes, o grupo
“rótulo”, recebeu uma aula introdutória de programação Figura 6.7 Configurando os pontos da variável para
na qual explicamos uma variável como sendo 0 no Scratch

2
“Thinking Out of the Box: Comparing Variables in Programming Education”, 2018, https://dl.acm.org/doi/
10.1145/3265757.3265765 .
Machine Translated by Google

108 CAPÍTULO 6 Melhorando na resolução de problemas de programação

um rótulo, como a temperatura ou a idade de uma pessoa. A outra metade, o grupo “caixa”, recebeu
uma aula em que explicamos as variáveis como sendo caixas, como um cofrinho ou uma caixa de
sapatos. Usamos consistentemente a mesma metáfora em ambas as lições; por exemplo, usamos a
frase “x contém 5?” para o grupo de caixas e “x é 5?” para o grupo de rótulos.
Após esta lição introdutória, os participantes foram testados em sua compreensão de programação.
Isso incluiu perguntas simples sobre variáveis com uma variável, mas também incluímos perguntas
em que uma variável foi definida duas vezes para investigar se os participantes entenderam que uma
variável pode conter apenas um valor.
Nossos resultados mostraram claramente que ambas as metáforas para variáveis têm seus
benefícios e desvantagens. Para questões simples relacionadas a variáveis com uma atribuição, o
grupo caixa se saiu melhor. Assumimos que pensar em uma caixa é uma coisa fácil de fazer, porque
as pessoas colocam coisas em caixas o tempo todo. Portanto, visualizar o conceito de uma caixa para
armazenamento de uma variável pode ajudá-los a entender a ideia. No entanto, quando analisamos
quantos participantes pensavam que uma variável poderia conter dois valores, descobrimos que as
pessoas do grupo da caixa provavelmente sofreriam com esse equívoco.
A conclusão importante de nosso estudo é que você deve ter cuidado ao descrever conceitos de
programação e o funcionamento correspondente do computador em termos de objetos e operações
no mundo real. Embora essas metáforas possam ser valiosas, elas também podem criar confusão,
especialmente porque modelos mentais antigos podem permanecer no LTM e, ocasionalmente,
aparecer na memória de trabalho.

EXERCÍCIO 6.6 Pense em uma máquina fictícia que você costuma usar ao raciocinar
sobre código ou ao explicar código. Quais são algumas das desvantagens ou limitações
dos modelos mentais que essa máquina nocional cria?

6.5 Máquinas e esquemas nocionais


Embora possa haver desvantagens no uso de máquinas fictícias, em geral elas funcionam bem como
um meio de pensar sobre programação. A razão para isso está relacionada a alguns tópicos que
abordamos anteriormente neste livro. Máquinas nocionais que funcionam bem relacionam conceitos
de programação a conceitos cotidianos para os quais as pessoas já formaram esquemas fortes.

6.5.1 Por que os esquemas são importantes

Esquemas são maneiras pelas quais o LTM armazena informações. Por exemplo, a ideia de uma
caixa é muito provavelmente um conceito com o qual as pessoas têm fortes associações. Colocar algo
em uma caixa, recuperá-lo mais tarde e abri-lo apenas para ver o que está dentro são operações
prováveis com as quais as pessoas estão familiarizadas. Como tal, pensar em uma variável como uma
caixa não causa carga cognitiva extra. Se disséssemos “Uma variável é como um monociclo”, isso
seria muito menos útil, precisamente porque a maioria das pessoas não tem um modelo mental forte
de todas as operações que um monociclo suporta.
O que você pode presumir que as pessoas saberão não é fixo no tempo e no lugar, é claro.
Ao explicar um conceito, portanto, é importante escolher uma comparação com a qual a pessoa para
quem você está explicando esteja familiarizada. Por exemplo, ao explicar a funcionalidade de um
computador para crianças na zona rural da Índia, alguns educadores usaram
Machine Translated by Google

Resumo 109

elefantes como computadores e seus treinadores como programadores, pois esse é um princípio
familiar para as crianças.

6.5.2 As máquinas nocionais são semânticas?

A maneira como defini máquinas nocionais pode lembrá-lo da definição da semântica de um


programa de computador. A semântica é o subcampo da ciência da computação preocupado com
o estudo do significado dos programas em vez de sua aparência, o que é chamado de sintaxe.
Você pode se perguntar se a máquina nocional de uma linguagem de programação simplesmente
indica sua semântica. No entanto, a semântica visa formalizar o funcionamento de um computador
em equações matemáticas e com precisão matemática.
Eles não visam abstrair detalhes, mas sim especificar os detalhes de forma precisa e completa.
Em outras palavras, máquinas nocionais não são simplesmente semânticas.

Resumo ÿ
Como você representa um problema pode influenciar fortemente a maneira como você pensa sobre ele.
Por exemplo, pensar nos clientes como uma lista em vez de uma coleção pode influenciar
como você armazena e analisa os objetos do cliente.
ÿ Modelos mentais são representações mentais que formamos enquanto pensamos em problemas.
As pessoas podem ter vários modelos mentais que podem competir entre si. ÿ Máquinas
nocionais são versões abstratas de como um computador real funciona que são usadas para
explicar conceitos de programação e raciocinar sobre programação.

ÿ Máquinas nocionais nos ajudam a entender a programação porque nos permitem


aplicar esquemas existentes à programação.
ÿ Diferentes máquinas nocionais às vezes se complementam bem, mas também podem criar
modelos mentais conflitantes.
Machine Translated by Google

Equívocos:
Erros no pensamento

Este capítulo abrange


ÿ Como o conhecimento de uma linguagem de programação pode
ajudá-lo a aprender um novo
ÿ Evitando problemas ao aprender um segundo
linguagem de programação
ÿ Compreender como o cérebro pode
equívocos e como equívocos levam a erros

ÿ Como evitar equívocos no pensamento e prevenir bugs

Nos últimos capítulos, abordamos técnicas para pensar sobre código, como
criando visualizações, usando frameworks para suportar a memória de trabalho e
usando modelos mentais para ajudar a resolver problemas de código. Não importa quão úteis
sejam as técnicas que usamos para apoiar nossos cérebros, às vezes cometemos erros em
pensando em código.

110
Machine Translated by Google

Por que aprender uma segunda linguagem de programação é mais fácil do que aprender a primeira 111

O foco deste capítulo são os bugs. Às vezes, os bugs são o resultado de desleixo, por
Por exemplo, quando você esquece de fechar um arquivo ou comete um erro de digitação em um nome de arquivo. Mais frequentemente,

no entanto, os bugs são o resultado de um erro de pensamento. Você pode não saber que um arquivo
precisa ser fechado após o uso, ou você pode supor que a linguagem de programação
fecha o arquivo para você automaticamente.
Neste capítulo, primeiro exploraremos o tópico de aprender programação múltipla
línguas. Existem muitas fontes possíveis de suposições errôneas quando você aprende
uma nova linguagem, sendo que diferentes linguagens de programação têm convenções diferentes para vários
conceitos. Python, por exemplo, fechará um arquivo após um bloco começando com open() sem exigir uma
instrução file.close() explícita , mas em C você
sempre tem que usar fclose(). Na primeira parte deste capítulo, mostrarei como melhor
use seu conhecimento existente para aprender novas linguagens de programação, bem como
evitar frustrações e erros resultantes de diferenças entre idiomas.
A segunda parte do capítulo ensina sobre suposições errôneas sobre
código. Abordaremos vários equívocos específicos de programação e nos aprofundaremos
as origens dos equívocos. Estar ciente de quais equívocos você pode ter
sobre o código irá ajudá-lo a detectar erros mais cedo e até mesmo evitar alguns.

7.1 Por que aprender uma segunda linguagem de programação


é mais fácil do que aprender o primeiro
Nos capítulos anteriores, você aprendeu que palavras-chave e modelos mentais armazenados em seu
O LTM pode ajudá-lo a compreender o código. Às vezes, quando você aprendeu alguma coisa,
esse conhecimento também é útil em outro domínio. Isso é chamado de transferência. A transferência acontece
quando as informações que você já conhece o ajudam a fazer coisas novas. Por exemplo, se
você já sabe jogar damas, será mais fácil aprender xadrez porque alguns
as regras são semelhantes. Da mesma forma, se você já conhece Java, aprender Python é mais fácil
porque você já conhece os conceitos básicos de programação, como variáveis, loops,
classes e métodos. Além disso, algumas das habilidades que você adquiriu durante a programação, como
como usar um depurador ou um criador de perfil, pode ser útil quando você está aprendendo a segunda linguagem
de programação.
Existem duas maneiras pelas quais o conhecimento de programação armazenado em seu LTM pode suportar
aprender novos conceitos de programação. Primeiro, se você já sabe muito sobre programação (ou qualquer
outro assunto), aprender mais sobre isso é mais fácil. Informações armazenadas em
seu LTM ajuda você a aprender coisas novas; isso é conhecido como transferência durante a aprendizagem.
Você aprendeu no capítulo 2 que quando você encontra novas informações, elas viajam de
a memória sensorial para o STM, e entra na memória de trabalho quando está sendo
processado. Este processo é ilustrado na figura 7.1. Quando você ativa seu trabalho
memória pensando em um novo conceito de programação, o LTM também é ativado e
inicia uma busca por informações relevantes.

Conforme mostrado na figura 7.1, quando seu LTM é pesquisado, as informações relacionadas ao
novas informações aprendidas podem ser encontradas. Se existirem informações relacionadas com o
novas informações, são retransmitidas para a memória de trabalho. Essas informações podem incluir
memórias procedurais, esquemas, planos ou memórias episódicas.
Machine Translated by Google

112 CAPÍTULO 7 Equívocos: Erros no pensamento

Memória de curto
Memória de
prazo longo prazo
Novo
Novo Relacionado
em formação
em formação
SM Trabalhando
memória

Novo

Relacionado

Figura 7.1 Quando você aprende uma nova informação, ela é processada primeiro pela memória sensorial (SM) e
depois pelo STM. As novas informações são posteriormente enviadas para a memória de trabalho, onde você
pode pensar sobre elas. Ao mesmo tempo, o LTM é pesquisado por informações relacionadas. Se forem
encontradas informações relacionadas, elas também são enviadas para a memória de trabalho para apoiar o pensamento sobre as novas inform

Por exemplo, quando você já conhece Java e está aprendendo sobre métodos em Python,
você pode ser lembrado de métodos em Java. Isso irá ajudá-lo a aprender o Python
métodos mais rapidamente, embora funcionem um pouco diferente dos métodos em Java.
No capítulo 3, falamos sobre o uso da elaboração ao aprender um novo conceito.
Elaboração é a prática de relacionar explicitamente novas informações com coisas que você já
conhecer. A razão pela qual a elaboração funciona bem é que pesquisar explicitamente em seu LTM por
informações relacionadas aumentam as chances de serem encontradas informações relevantes que
pode ajudá-lo a realizar a tarefa em mãos. Portanto, a elaboração pode ajudar a aumentar
transferência durante o aprendizado.

EXERCÍCIO 7.1 Pense em um novo conceito de programação ou biblioteca que você aprendeu
recentemente. Quais conceitos que você já conhecia ajudaram você a aprender o novo conceito?

A segunda maneira pela qual o conhecimento armazenado em seu LTM pode apoiar o aprendizado é chamado de transferência
de aprendizagem. A transferência de aprendizado acontece quando você pode aplicar coisas que já sabe
em situações totalmente desconhecidas. Quando as pessoas estão falando sobre ciência cognitiva e
usar o termo “transferência”, eles quase sempre significam transferência de aprendizado.
Às vezes, a transferência de aprendizado acontece sem que você pense nisso. Por
Por exemplo, quando você compra um novo par de calças, não precisa pensar em como
feche seu botão. Você apenas sabe como fazê-lo, mesmo que essas calças em particular e seus
botão não lhe são familiares. Da mesma forma, quando você compra um novo laptop, você sabe como é
O teclado funciona sem pensar, mesmo que você nunca tenha operado esse tipo específico de laptop antes.
A transferência de aprendizado também pode acontecer conscientemente, como quando
você aprende uma nova linguagem de programação. Se você está aprendendo JavaScript e já
conhece Python, você pode pensar explicitamente: “Eu sei que preciso recuar o corpo de um loop
em Python; isso seria verdade para JavaScript também?”
Machine Translated by Google

Por que aprender uma segunda linguagem de programação é mais fácil do que aprender a primeira 113

A transferência de aprendizagem é semelhante à transferência durante a aprendizagem porque em ambas as situações


ções o cérebro pesquisa o LTM procurando estratégias relevantes para aplicar.

7.1.1 Como aumentar as chances de se beneficiar do


conhecimento de programação existente
Como programador profissional, tenho certeza de que você já esteve em situações em que o conhecimento
poderia ter sido transferido, mas não foi. Talvez você estivesse confuso sobre como funcionava alguma
função em uma determinada biblioteca, e mais tarde acabou sendo exatamente a mesma que em uma
biblioteca que você já conhecia. Infelizmente, nem todo conhecimento útil será automaticamente transferido
para novas situações.
A quantidade de aprendizado que você pode transferir de uma tarefa para outra pode variar muito e é
influenciada por muitos fatores. Fatores que podem influenciar a quantidade de transferência que ocorre
incluem o seguinte:

ÿ Domínio — Quão bem você dominou a tarefa para a qual o conhecimento já está armazenado em
seu LTM. Quanto melhor você conhecer uma tarefa, maior a probabilidade de aplicá-la em outro
domínio. Por exemplo, um programador especialista em Java provavelmente se beneficiará mais
de seu conhecimento prévio ao aprender Python do que um programador Java iniciante. Como
vimos nos capítulos anteriores, um programador especialista tem um arsenal maior de estratégias,
blocos e modelos mentais que podem ser aplicados a problemas em qualquer linguagem de
programação diferente.
ÿ Semelhança — Semelhanças entre as duas tarefas. Por exemplo, se você estiver implementando
um algoritmo que já conhece em uma linguagem de programação desconhecida, será mais fácil
do que implementar um novo algoritmo em uma nova linguagem de programação.

ÿ Contexto — Quão semelhantes são os ambientes. Não é apenas a semelhança entre as tarefas que
importa, mas também o contexto em que você as está executando.
Por exemplo, a transferência entre duas linguagens de programação é mais provável se você
programar essas linguagens diferentes no mesmo IDE, o que é um forte argumento para usar um
IDE para vários idiomas. No entanto, o contexto se estende além do computador; também pode
importar se você está sentado no mesmo escritório com as mesmas pessoas. Quanto mais
semelhanças houver, mais provável é que o conhecimento seja transferido. ÿ Atributos críticos—
Quão claro está para você qual conhecimento pode ser benéfico. Se alguém apontar que conhecer
JavaScript pode ser benéfico para aprender Python, é mais provável que você procure ativamente por
semelhanças. Portanto, pode ser importante procurar ativamente por semelhanças e refletir sobre
elas antes de aprender uma nova linguagem de programação ou estrutura. Que conhecimento
existente poderia ajudar com a nova tarefa?

ÿ Associação—Com que intensidade você sente que as tarefas são semelhantes. Por exemplo, Java
e JavaScript soam semelhantes, embora conceitualmente as linguagens não sejam tão parecidas.
Portanto, pode existir uma associação mais forte em seu LTM entre Java e JavaScript do que entre
Python e Scala. Sua memória episódica,
Machine Translated by Google

114 CAPÍTULO 7 Equívocos: Erros no pensamento

que armazena memórias de coisas que você experimentou, também pode desempenhar um papel. Por
exemplo, se você aprendeu Java e C# na mesma sala de aula, pode conectá-los mais fortemente do que se
os tivesse aprendido em configurações diferentes. ÿ Emoções—Como você se sente em relação à tarefa.
Suas emoções também podem desempenhar um papel na probabilidade de transferência. Por exemplo, se você
achar prazeroso trabalhar com árvores binárias e uma nova tarefa o lembrar disso, você pode tentar aplicar
estratégias semelhantes à nova tarefa de forma mais ativa.

7.1.2 Diferentes formas de transferência


Existem várias maneiras diferentes de analisar a transferência. Se você tiver um vocabulário para as diferentes
formas de transferência, poderá definir expectativas mais realistas de transferência entre linguagens de programação.
Os programadores às vezes assumem que a sintaxe das linguagens de programação é irrelevante e que, se você
conhece uma linguagem, pode pegar uma segunda com facilidade e uma terceira sem nenhum esforço. Certamente,
conhecer um idioma pode facilitar o aprendizado de um novo, mas nem sempre ajuda. Compreender as diferentes
formas de transferência irá prepará-lo para aprender um novo idioma ou trabalho de enquadramento de forma mais
eficaz.

TRANSFERÊNCIA POR ESTRADA ALTA E BAIXA

A transferência de habilidades automatizadas é diferente da transferência de habilidades que você domina


conscientemente. A transferência de habilidades automatizadas é chamada de transferência de baixa rota. Na
programação, a transferência de baixo custo pode ocorrer se você usar Ctrl-C e Ctrl-V em um novo editor sem pensar
nisso. A transferência de tarefas mais complexas é chamada de transferência por estrada. Quando ocorre uma
transferência por estrada, você geralmente está ciente de que está acontecendo. Por exemplo, você pode supor que
precisa declarar uma variável em uma nova linguagem de programação porque sabe que esse é o caso na maioria
das linguagens.

TRANSFERÊNCIA PERTO E DISTANTE

Anteriormente, você aprendeu que, se dois domínios forem mais semelhantes, isso afetará a quantidade de
transferência. Observar a distância entre os domínios é outra maneira de dividir diferentes formas de transferência. A
transferência próxima ocorre quando o conhecimento é transferido de domínios que são vistos como próximos, como
cálculo e álgebra ou C# e Java.
Falamos de transferência distante quando as habilidades são transferidas entre domínios muito diferentes, como
latim e lógica, ou Java e Prolog. Como a semelhança é um fator que influencia a transferência, a transferência para
longe é muito menos provável de acontecer do que a transferência para perto.

EXERCÍCIO 7.2 Pense em algumas situações em que você passou por uma transferência.
Que forma de transferência ocorreu? Preencha a tabela a seguir para orientar seu pensamento.
Machine Translated by Google

Por que aprender uma segunda linguagem de programação é mais fácil do que aprender a primeira 115

Situação Auto estrada Estrada baixa Perto Distante

7.1.3 Já sabe alguma coisa: Maldição ou bênção?


Além da alta e baixa estrada e transferência perto e longe, há mais duas
categorias de transferência. O tipo de transferência que vimos até agora, em que saber alguma coisa dá suporte
ao aprendizado de uma coisa nova ou à realização de uma nova tarefa, é chamado de transferência positiva.

Quando ocorre uma transferência positiva, você não precisa criar um novo modelo mental
do princípio; em vez disso, seu cérebro recebe ajuda na construção de um modelo mental de um novo
situação, baseando-a em modelos mentais que o LTM já mantém para outros domínios. Por
Por exemplo, quando você conhece Java, você já tem um modelo mental de um loop e sabe
que um loop tem uma variável de contador, um corpo e uma condição de parada. Em quase todos os novos
linguagem de programação que você encontrar, os loops também terão esses aspectos, então você sabe
o que procurar. Isso ajuda você a criar um novo modelo mental. No entanto, como você pode
experimentaram, a transferência nem sempre é positiva. Quando o conhecimento existente impede
você de aprender algo novo, chamamos isso de transferência negativa. Edsger W. Dijkstra,
Professor holandês de ciência da computação e criador do algoritmo de Dijkstra, disse
que ensinar BASIC deveria ser proibido porque “aleija a mente”.
Embora eu não acredite de forma alguma que os cérebros possam ser arruinados para sempre aprendendo um certo
linguagem de programação, há alguma verdade neste ditado porque os erros podem ser
causados por suposições erradas sobre o código. Suposições erradas podem, por sua vez, ser causadas
por transferência negativa. Por exemplo, em Java, as variáveis não podem ser usadas sem inicialização. Um
programador Java experiente pode supor que em Python também, todas as variáveis
deve ser inicializado e que o compilador irá avisá-los se eles esquecerem. Isso pode causar
confusão e levar a bugs.
Mesmo em linguagens muito semelhantes, como Java e C#, há riscos de transferência negativa
porque os modelos mentais de ambas as línguas são semelhantes, mas não idênticos. Por exemplo, Java tem
um conceito chamado exceções verificadas, que são exceções verificadas em tempo de compilação. Se você não
encapsular essas exceções em um bloco try-catch , o código falhará em
compilar. As exceções verificadas são um recurso de linguagem específico do Java, portanto, as pessoas que vêm
do C# podem não perceber que são diferentes do que estão acostumados. Não apenas faça
eles têm o modelo mental errado, mas acham que têm o certo!
Machine Translated by Google

116 CAPÍTULO 7 Equívocos: Erros no pensamento

Esquecer de inicializar uma variável ou manipular uma exceção são relativamente pequenos
erros que podem ser facilmente corrigidos. Mas há exemplos de transferência negativa que correm
Deeper. Por exemplo, muitas pessoas têm dificuldade em aprender uma linguagem funcional
como F# quando já estão familiarizados com linguagens orientadas a objetos porque as funções existem em
ambos os paradigmas, mas funcionam de forma diferente.

EXERCÍCIO 7.3 Pense em uma situação em que você fez uma suposição incorreta
sobre um conceito de linguagem em uma linguagem de programação. Poderia ser isso
atribuída à transferência negativa de uma língua para outra?

7.1.4 As dificuldades de transferência


Na seção anterior, você viu que a transferência de conhecimento pode ser negativa e
positivo e que a transferência positiva não é um dado. As situações precisam ser semelhantes o suficiente
entre si para tornar a transferência mais provável. Transferência distante, em que o conhecimento de um
domínio “salta” para um domínio que não é muito semelhante ao domínio original, é
improvável que aconteça espontaneamente.
Infelizmente, a pesquisa mostra que a transferência é realmente difícil e não vem automaticamente
para a maioria das pessoas. O xadrez é frequentemente apontado como uma fonte candidata para transferência
- muitas pessoas acreditam que o conhecimento de xadrez melhorará a inteligência geral, bem como a lógica.
habilidades de raciocínio e memória. No entanto, estudos científicos não foram capazes de apoiar essas
suposições. No capítulo 2 discutimos o trabalho de Adriaan de Groot,
cujos experimentos mostraram que a memória dos jogadores de xadrez experientes não era melhor do que a
dos novatos quando as configurações eram aleatórias. Outros estudos confirmaram
isso e mostrou que jogadores de xadrez proficientes não são necessariamente melhores em lembrar números
ou formas visuais. Habilidades de xadrez não parecem ser transferidas para outras
jogos também, como a Torre de Londres (um quebra-cabeça semelhante às Torres de Hanói).
O que é verdade para o xadrez parece ser verdade para a programação também. Muitos programadores
argumentam que ao aprender a programar você ganhará habilidades de raciocínio lógico, ou mesmo
aumentar sua inteligência geral. No entanto, o pequeno número de estudos que
pesquisados os efeitos cognitivos da programação mostram um padrão semelhante ao encontrado
com xadrez. Um estudo geral feito em 1987 por Gavriel Salomon da Universidade de Tel
Aviv revelou que a maioria dos estudos sobre o impacto da educação em programação mostra pouco
efeito. Muitos dos estudos examinados por Salomon mostram que as crianças são bem sucedidas
na aquisição de certas habilidades de programação, mas essas habilidades não parecem ser transferidas para
outros domínios cognitivos.
A lição aqui é que o fato de você já dominar uma programação
idioma nem sempre o ajudará a aprender um novo. Isso pode ser frustrante porque
você já se vê como um especialista, e o ritmo lento de um iniciante e as atividades de aprendizado
correspondentes, como usar flashcards para aprender sintaxe, podem parecer desnecessárias. UMA
O conselho relacionado a considerar é que, se você pretende aprender um novo idioma para
expandir sua maneira de pensar, é importante escolher uma linguagem que seja fundamentalmente
diferente dos que você já domina - ou seja, você deve evitar um falso
ampliação de seus gostos de “música country” para “música ocidental”.
Machine Translated by Google

Equívocos: Erros no pensamento 117

No entanto, esta seção mostrou que a transferência distante, digamos, de algo como SQL para
JavaScript, não é tão provável, e você provavelmente precisará aprender muito
sintaxe, bem como novas estratégias para recuperar seu nível de especialista em uma nova linguagem de
programação. As práticas também podem diferir; por exemplo, muito do que você sabe sobre reutilização
e a abstração em JavaScript deve ser considerada de maneira bastante diferente em SQL.
Prestar atenção deliberada às semelhanças e diferenças facilitará a tarefa de
aprendendo um novo idioma.

EXERCÍCIO 7.4 Considere uma linguagem de programação que você está aprendendo ou uma que você
quero aprender. Compare-o com o(s) idioma(s) que você já conhece. O que é semelhante
e o que é diferente?

O preenchimento da tabela a seguir pode ajudar a simplificar seu pensamento e mostrar


onde você pode esperar a transferência e onde você precisará prestar atenção específica enquanto
aprende.

Semelhanças Diferenças Observações

Sintaxe

Tipo de sistema

Conceitos de programação

Tempo de execução

Ambiente de programação/
IDE

Ambiente/práticas de teste

7.2 Equívocos: Erros no pensamento


Na primeira parte deste capítulo, nos concentramos em como o conhecimento é transferido de uma situação
para outra. Quando as informações armazenadas interferem na execução de uma nova tarefa,
chamamos isso de transferência negativa. Nesta seção, exploraremos as consequências da negação
transferência ativa.

Pense em uma situação em que você criou um bug, por exemplo, esquecendo de
inicialize uma instância da maneira correta, chamando a função errada ou com um erro off-by one em uma
lista. Situações em que ocorrem bugs podem ser causadas por um simples deslize do
lembre-se de onde você acidentalmente esqueceu algum código, selecionou o método errado ou fez um
erro ao calcular um valor limite.
No entanto, os bugs também podem ter uma razão subjacente mais profunda, onde você cometeu um erro
suposição sobre o código em mãos. Talvez você tenha contado com o fato de que a instância
seria inicializado em outro lugar no código, ou talvez você tivesse certeza de que era o correto
método ou assumiu que a estrutura de dados impediria você de acessar elementos
Machine Translated by Google

118 CAPÍTULO 7 Equívocos: Erros no pensamento

fora de seus limites. Quando você tem certeza de que seu código funcionará, mas ainda falha, é
provável que você esteja sofrendo de um equívoco.
Na conversa normal, a palavra “equívoco” é frequentemente usada como sinônimo de erro ou
confusão, mas a definição formal é um pouco diferente. Para que uma crença seja um equívoco, ela
deve ÿ ser defeituosa, ÿ ser mantida de forma consistente, em diferentes situações, e ÿ ser mantida

com confiança.

Existem muitos equívocos comuns. Por exemplo, muitas pessoas pensam que as sementes de pimenta
são a parte mais picante. As sementes de pimentão não são nada picantes!
Este é um exemplo de um equívoco porque

1 está com defeito

2 Se as pessoas acreditam que as sementes de um tipo de pimenta são picantes, elas vão acreditar
sementes de todos os tipos de pimenta são picantes, e

3 Eles acreditam que é verdade e agem de acordo, por exemplo, removendo as sementes das pimentas antes de
cozinhá-las.

O equívoco “sementes de pimentas são picantes” é o resultado de uma lenda urbana que as pessoas
simplesmente repetiram umas às outras, mas a transferência negativa muitas vezes desempenha um
papel na criação de equívocos. Por exemplo, muitas pessoas acreditam que a carne selando vai “selar
os sucos” porque as superfícies externas de outras formas de alimentos, como ovos, solidificam
quando aquecidas. Essas pessoas supõem que o calor sempre cria um “escudo” sólido, que será
impenetrável pela umidade presa no interior. O conhecimento de um tipo de alimento é incorretamente
transferido para outro tipo de alimento, levando a um equívoco – de fato, a carne selada leva a uma
maior perda líquida de umidade.
Equívocos comumente ocorrem na programação também. Novos programadores algumas vezes
assumem que uma variável, como temperatura, pode conter apenas um valor que não pode ser
alterado. Embora essa suposição possa parecer absurda para um programador experiente, há razões
pelas quais assumir que uma variável pode conter apenas um valor é sensato. Por exemplo, essa
suposição pode ser transferida do conhecimento prévio de matemática, onde as variáveis de fato não
mudam dentro do escopo de uma prova matemática ou
exercício.
Outra fonte desse equívoco está no domínio da própria programação.
Já vi alunos com exposição prévia a arquivos e sistemas de arquivos transferirem incorretamente
crenças sobre arquivos para variáveis. Como os sistemas operacionais normalmente permitem que
você crie apenas um arquivo com um determinado nome (em uma pasta), os alunos podem assumir
incorretamente que a variável temperature já está em uso e não pode conter um segundo valor, assim
como um nome de arquivo só pode ser usado para um arquivo.

7.2.1 Depurando equívocos com mudança conceitual


Equívocos são maneiras de pensar defeituosas mantidas com grande confiança. Como as concepções
erradas são mantidas com tanta confiança, pode ser difícil mudar a opinião de alguém.
Machine Translated by Google

Equívocos: Erros no pensamento 119

mente sobre eles. Muitas vezes, não é suficiente apontar a falha em seu pensamento.
Em vez disso, para mudar um equívoco, a maneira de pensar defeituosa precisa ser substituída
com uma nova forma de pensar. Ou seja, não basta dizer a um programador iniciante que um
variável pode ser alterada; eles precisam obter uma nova compreensão da variável como um
conceito.
O processo de substituir um equívoco baseado em uma linguagem de programação que você
já sabe com o modelo mental certo para o novo idioma que você está aprendendo é
chamada mudança conceitual. Nesse paradigma, uma concepção existente é fundamentalmente
alterado, substituído ou assimilado pelo novo conhecimento. É essa mudança no conhecimento, mais do que a
adição de novos conhecimentos a um esquema já existente, que
distingue a mudança conceitual de outros tipos de aprendizagem.
O fato de que o conhecimento que você já aprendeu precisa ser mudado em sua
O LTM torna o aprendizado de mudança conceitual mais difícil do que o aprendizado regular. É por isso que
equívocos podem durar muito tempo; simplesmente receber informações sobre por que seu pensamento está errado
muitas vezes não ajuda ou não ajuda o suficiente.
Portanto, você terá que gastar muita energia quando aprender uma nova linguagem de programação
“desaprendendo” o conhecimento existente sobre linguagens de programação anteriores.
Por exemplo, se você está aprendendo Python quando já conhece Java, terá que
desaprender alguma sintaxe, assim você deve sempre definir os tipos de variáveis. Você também vai
tem que desaprender algumas práticas, como confiar nos tipos de variáveis para tomar decisões
em código. Mesmo que não seja difícil lembrar o simples fato de que Python é tipado dinamicamente, aprender a
pensar sobre tipos enquanto você está programando pode levar mais tempo porque requer mudança conceitual.

7.2.2 Suprimindo equívocos


Lembra quando pensamos em vestir um boneco de neve com um suéter e pedi para você
decidir se o boneco de neve derreteria mais rápido ou mais devagar do que quando ele está “nu”? Você primeiro

provavelmente era que ele iria derreter mais rápido, afinal, colocar um suéter faz
um mais quente, certo? Bem, não para um boneco de neve. Ao usar um suéter, o boneco de neve é
isolado, prendendo no frio e retardando o processo de fusão.
O que provavelmente aconteceu é que seu cérebro ativou instantaneamente uma concepção existente:
suéteres deixam você mais quente. Essa concepção, que é correta nas pessoas (de sangue quente), foi então
erroneamente transferida para a situação do boneco de neve. Isso não é porque
você não é uma pessoa inteligente!
Foi por muito tempo assumido que quando as pessoas aprendem como as coisas funcionam, velhas, incorretas
as noções são apagadas definitivamente de suas memórias e substituídas por noções melhores e mais corretas.
Sabendo o que sabemos sobre o cérebro, isso é improvável. As memórias são
agora assumido para não ser esquecido ou substituído; em vez disso, nossa recuperação deles diminui
hora extra. No entanto, as velhas memórias de maneiras erradas de pensar ainda estão lá, e
caminhos para eles podem ser acionados, mesmo se preferirmos que não sejam.
A pesquisa mostrou que as pessoas muitas vezes recorrem a noções antigas, mesmo que possam
também trabalhar com sucesso com os corretos. O trabalho de Igal Galili e Varda Bar da
Machine Translated by Google

120 CAPÍTULO 7 Equívocos: Erros no pensamento

a Universidade Hebraica de Jerusalém mostrou que os alunos podiam trabalhar bem com
mecânica em exercícios familiares, mas regrediam a um raciocínio mais básico, mas errado,
para questões mais complicadas.1 Isso ilustra que várias noções podem estar presentes na
memória ao mesmo tempo, como no boneco de neve exemplo: podemos ter a ideia de que
um suéter significa calor em nossos cérebros, assim como a noção de que um suéter isola e,
portanto, mantém o frio. Essas ideias competem umas com as outras quando temos que
decidir se um suéter aquece um boneco de neve, então temos que suprimir ativamente a
ideia mais antiga de que suéter é igual a calor para chegar à conclusão certa. Isso
provavelmente aconteceu quando você pensou no quebra-cabeça e experimentou um
momento de “espere um minuto” em que raciocinou em vez de reagir intuitivamente.
Não sabemos exatamente como o cérebro decide qual dos conceitos armazenados usar,
mas sabemos que a inibição desempenha um papel. Normalmente associamos a inibição a
um sentimento de autoconsciência, contenção ou timidez. No entanto, pesquisas recentes
começaram a indicar que, quando mecanismos de controle inibitórios estão ativos, uma
concepção incorreta pode perder a concorrência com uma concepção correta.

EXERCÍCIO 7.5 Pense em uma situação em que você teve um conceito errado sobre um
conceito em uma determinada linguagem de programação. Por exemplo, eu assumi por um
tempo embaraçosamente longo que todas as linguagens funcionais usavam avaliação
preguiçosa e que todas as linguagens preguiçosas deveriam ser funcionais porque eu só
conhecia uma linguagem preguiçosa e funcional: Haskell. Que equívoco você manteve por
muito tempo, e qual foi sua origem?

7.2.3 Equívocos sobre linguagens de programação


Extensa pesquisa foi feita sobre equívocos no domínio da programação, especialmente os
equívocos que os programadores novatos têm. Juha Sorva, agora professora universitária
sênior da Universidade de Aalto, na Finlândia, escreveu uma dissertação de doutorado em
2012 que contém uma lista de 162 diferentes equívocos que os novatos podem ter.2 Todos
esses equívocos estão enraizados na pesquisa. Embora a lista completa seja muito
interessante, e eu recomendo a leitura, alguns dos equívocos na tese de Sorva são
especialmente dignos de nota: ÿ Equívoco 15: A atribuição primitiva armazena equações ou
expressões não resolvidas. Esse equívoco indica que às vezes as pessoas assumem
que atribuições de variáveis armazenam relacionamentos. Uma pessoa que sofre
desse equívoco assume que se escrevermos total = máximo + 12 , de alguma forma,
liga o valor de total com o valor
valor máximo.
Isso leva à crença de que se o máximo fosse alterado posteriormente no código, o
total mudaria junto com ele. O interessante sobre esse equívoco é que ele é muito
sensato. Você pode imaginar uma linguagem de programação em

1
Veja “Movimento Implica Força: Onde Esperar Vestígios do Equívoco?” por Igal Gaili e Varda Bar, 1992,
https://www.tandfonline.com/doi/abs/10.1080/0950069920140107.
2
Consulte a tabela A-1 (pp. 3593–68) de “Visual Program Simulation in Introductory Programming Education”,
http://lib.tkk.fi/Diss/2012/isbn9789526046266/isbn9789526046266.pdf.
Machine Translated by Google

Equívocos: Erros no pensamento 121

em que as relações entre as variáveis podem ser expressas como um sistema de


equações. Existem até linguagens de programação que funcionam dessa forma até certo ponto,
como o Prolog.
Esse equívoco geralmente ocorre em pessoas com conhecimento matemático prévio. Um
equívoco relacionado que abordamos anteriormente neste capítulo é a ideia de que
uma variável pode conter apenas um valor. Isso também é verdade na matemática.
ÿ Equívoco 33: Os loops while terminam assim que a condição muda para false. Essa concepção
equivocada expressa confusão sobre quando a condição de parada de um loop while
é avaliado. Ao sofrer com esse equívoco, as pessoas assumem o loop
condição é verificada em cada linha e que pára imediatamente quando a condição é falsa e não
termina a execução. Esse equívoco provavelmente
relação com o significado atribuído à palavra-chave while. Quando ouvimos
alguém diz: “Vou sentar aqui e ler meu livro enquanto está chovendo”, presumimos
o falante desta frase monitora regularmente o clima, sai quando para
chovendo, e não termina primeiro o livro inteiro. Aqui, também, o equívoco
não é um sinal de que uma pessoa está totalmente confusa e não tem noção de como
programação funciona. É uma suposição bastante razoável pensar que o código é tão
semelhante ao inglês se comportaria da mesma forma.
Este equívoco é um exemplo de onde o significado do inglês
(chave)palavras interferem na compreensão da programação. Além disso, você poderia novamente
imagine uma linguagem de programação na qual a condição de parada de um loop while é
avaliada continuamente e o loop para imediatamente quando a condição
torna-se falso.

Um equívoco relacionado é assumir que o nome da variável influencia


que valor ela pode conter - por exemplo, pensar que uma variável chamada mínimo
nunca pode conter um valor grande (este é o equívoco 17 na lista de Sorva).
ÿ Equívoco 46: A passagem de parâmetros requer nomes de variáveis diferentes na chamada e na
assinatura. Pessoas com esse equívoco tendem a supor que um nome de variável pode
ser usado apenas uma vez, incluindo funções internas. Quando você está aprendendo a programar,
você aprende que nomes de variáveis só podem ser usados uma vez. Se uma nova variável for
necessário, você precisa definir um novo nome também. A limitação de um uso de variáveis, no
entanto, não é mais verdadeira quando falamos de métodos ou funções e
suas invocações. De repente, é permitido usar o mesmo nome tanto dentro de um
função e fora dela. Na verdade, é mais do que permitido; usando os mesmos nomes
para diferentes variáveis dentro e fora de funções é uma prática comum. Isto é
frequentemente mostrado em exemplos introdutórios de funções. Por exemplo, código como este
é bastante comum quando se ensina funções:

def quadrado(número):
retornar número * número

número = 12
print(quadrado(número))
Machine Translated by Google

122 CAPÍTULO 7 Equívocos: Erros no pensamento

Esse código também ocorre na vida real. Por exemplo, quando você usa a extração
funcionalidade de método em um IDE, a maioria dos IDEs replicará o nome da variável tanto
ao definir e chamar a função. Portanto, o código real é preenchido com esse padrão
também, o que pode ser o motivo pelo qual os educadores o usam. Este equívoco é interessante
exemplo de um equívoco que transfere dentro de uma linguagem de programação
em vez de ser influenciado pelo conhecimento prévio de matemática ou inglês. Algumas vezes, quando
entendemos um conceito específico de uma linguagem de programação, o
conhecimento que adquirimos sobre essa parte não se transfere nem mesmo para outros conceitos
dentro da mesma língua.

7.2.4 Prevenindo equívocos ao aprender uma nova linguagem de programação


Não há muito que possamos fazer sobre equívocos. Ao aprender uma nova linguagem ou sistema de
programação, inevitavelmente você será confrontado com uma transferência negativa.
No entanto, existem algumas estratégias que podem ser úteis.
Em primeiro lugar, é importante estar ciente de que, mesmo que você tenha certeza de que conseguiu algo
certo, você ainda pode estar errado. Manter a mente aberta é fundamental.
Segundo, você pode estudar deliberadamente equívocos comuns para evitar
de cair presa deles. Pode ser difícil saber quando você está cometendo erros
suposições e quais suposições são válidas. Portanto, pode ser útil usar um
lista de equívocos comuns. O exercício 7.5 pode ajudá-lo a ter uma primeira noção de
áreas potenciais onde você pode ter equívocos, e a lista de Sorva pode ser usada
ao aprender uma nova linguagem de programação como uma diretriz para determinar quais concepções
erradas você deve observar. Use a lista para identificar equívocos que
poderia aplicar à linguagem de programação que você está aprendendo.
Uma dica final é pedir conselhos a programadores que também aprenderam as mesmas linguagens de
programação na mesma ordem. Cada par de linguagens de programação tem sua própria
interações que podem criar equívocos, então há muitas para listar aqui. Perguntando
o conselho de pessoas que podem ter encontrado as mesmas armadilhas pode ser tremendamente útil.

7.2.5 Diagnosticando equívocos em uma nova base de código


Nesta seção, falamos principalmente sobre equívocos em linguagens de programação em geral, que podem
ser causados por conhecimento prévio que se transfere negativamente para
uma nova linguagem de programação.
Da mesma forma, você pode ter equívocos sobre a base de código em que está trabalhando.
Sempre que você faz suposições sobre o código com base na experiência anterior com uma linguagem de
programação, estrutura ou biblioteca, ou sobre o domínio do código, o
significados de variáveis ou outros nomes, ou as intenções de outros programadores, há
um risco de equívoco.
Uma maneira de detectar equívocos é programar em pares ou em um grupo maior. Se
você se expõe às ideias e suposições dos outros, logo ficará claro que
há conflitos e que uma pessoa tem um equívoco.
Machine Translated by Google

Resumo 123

Especialmente para programadores experientes (ou especialistas em qualquer coisa), pode ser
difícil perceber que o erro é seu, então sempre verifique as suposições que você faz sobre o código
executando-o ou fazendo uso de um conjunto de testes. Se você tem certeza de que um determinado
valor nunca pode ser inferior a zero, por que não adicionar um teste para verificá-lo? Esse teste pode
não apenas ajudá-lo a detectar se sua suposição está errada, mas também serve como documentação
do fato de que o valor de fato sempre será positivo. O teste pode comunicar essa informação a você no
futuro, o que é importante porque, como vimos, os equívocos raramente desaparecem e sempre podem
ressurgir, mesmo quando aprendemos o modelo correto.
Como isso sugere, a documentação é uma terceira maneira de combater equívocos sobre um
determinado método, função ou estrutura de dados dentro de uma base de código. Se você descobriu
um equívoco, além de adicionar um teste, você também pode querer adicionar documentação em locais
relevantes para evitar que você e outras pessoas caiam na mesma armadilha.

Resumo ÿ O
conhecimento que você já armazenou em seu LTM pode ser transferido para novas situações.
Às vezes, o conhecimento existente ajuda você a aprender mais rápido ou a realizar novas
tarefas melhor. Isso é chamado de transferência positiva.
ÿ A transferência de conhecimento de um domínio para outro também pode ser negativa, o que
acontece quando o conhecimento existente interfere no aprendizado de coisas novas ou na
execução de novas tarefas. ÿ Você pode usar a transferência positiva para aprender coisas
novas de forma mais eficaz pesquisando ativamente informações relacionadas em seu LTM (por
exemplo, por elaboração, conforme abordado anteriormente neste livro).

ÿ Você pode ter equívocos, que ocorrem quando você tem certeza de que está certo, mas na
verdade está errado. ÿ Os equívocos nem sempre são resolvidos simplesmente percebendo ou
sendo informado de que você está errado. Para que os equívocos sejam corrigidos, você precisa de
um novo modelo mental para substituir o antigo modelo errado.

ÿ Mesmo que você tenha aprendido um modelo correto, sempre existe o risco de você voltar a usar
o equívoco. ÿ Use testes e documentação em uma base de código para ajudar a evitar equívocos.
Machine Translated by Google
Machine Translated by Google

Parte 3

Sobre escrever código melhor

Dentro
partes 1 e 2, examinamos os papéis da memória de curto prazo, da
memória de longo prazo e da memória de trabalho ao ler e pensar sobre código.
Na parte 3, movemos nossa atenção para escrever código melhor: como escrever código
que seja compreensível e evite nomes vagos e cheiros de código. Também discutiremos
como melhorar suas habilidades em escrever código para problemas complexos.
Machine Translated by Google
Machine Translated by Google

Como ficar melhor


em nomear as coisas

Este capítulo abrange


ÿ Comparando diferentes perspectivas sobre boas práticas de
nomenclatura
ÿ Compreender a relação entre nomes
e processos cognitivos
ÿ Explorando o efeito de diferentes estilos de
nomenclatura ÿ Investigando o efeito de nomes ruins
em bugs e erros
ÿ Aprender como estruturar um nome de variável para
maximizar a compreensão

A Parte 1 cobriu os diferentes processos cognitivos envolvidos na leitura de código, incluindo


armazenar informações no LTM e recuperá-las quando necessário, armazenando informações
no STM e código de processamento na memória de trabalho. Na parte 2, vimos
como pensamos sobre código, quais modelos mentais são formados sobre código e como
falar sobre código. Na Parte 3, vamos ampliar o processo de escrever código em vez de
lendo ou pensando sobre isso.

127
Machine Translated by Google

128 CAPÍTULO 8 Como melhorar em nomear as coisas

Este capítulo tem como objetivo estudar como nomear melhor as coisas no código, como variáveis, classes,
e métodos. Como agora sabemos bastante sobre como o cérebro processa o código,
pode entender mais profundamente por que a nomenclatura é tão importante para a compreensão do código.
Bons nomes ajudam a ativar seu LTM para encontrar informações relevantes que você já conhece
sobre o domínio do código. Nomes ruins, por outro lado, podem fazer com que você faça
suposições sobre o código, levando a equívocos.
Embora nomear seja importante, também é muito difícil. Os nomes são frequentemente criados enquanto
modelar uma solução ou resolver um problema. Durante tal atividade, é provável que você esteja
experimentando uma alta carga cognitiva: sua memória de trabalho está em plena capacidade para criar um
modelo mental e usar o modelo mental para raciocinar. Nessa situação, pensar
um bom nome de variável pode simplesmente causar muita carga cognitiva, que seu cérebro
quer prevenir. Como tal, faz sentido, de uma perspectiva cognitiva, escolher um
nome ou um nome de espaço reservado para não exceder a capacidade da memória de trabalho.
Este capítulo cobre a importância, mas também a dificuldade de nomear em profundidade.
Uma vez que tenhamos coberto os conceitos básicos de nomenclatura e processamento cognitivo, ampliaremos
no efeito de nomes em dois aspectos diferentes da programação. Vamos estudar primeiro
que tipos de nomes tornam o código mais fácil de compreender. Em segundo lugar, veremos o
efeito de nomes ruins na ocorrência de bugs. Fechamos o capítulo com concreto
orientações para a criação de grandes nomes.

8.1 Por que a nomenclatura é importante


Escolher um bom nome de variável é difícil. Phil Karlton, programador da Netscape,
famosamente disse que existem apenas dois problemas difíceis em ciência da computação: invalidação de
cache e nomeação de coisas. E, de fato, muitos programadores lutam para nomear
coisas.
Representar tudo o que uma classe ou estrutura de dados faz em uma palavra inequívoca é
não é uma tarefa fácil. Dror Feitelson, Berthold Badler professor de ciência da computação na
Universidade Hebraica de Jerusalém, recentemente realizou um experimento para entender exatamente como
difícil é chegar a um nome inequívoco. Feitelson realizou um experimento
em que quase 350 sujeitos foram convidados a escolher nomes em diferentes programações
cenários. Os sujeitos do estudo foram estudantes e profissionais de programação
com uma média de seis anos de experiência profissional. Os participantes foram convidados a escolher nomes
para variáveis, constantes e estruturas de dados, e também para funções e seus parâmetros. A experiência de
Feitelson confirmou que nomear é difícil, ou pelo menos que é difícil
escolha nomes que outros também escolhem. No experimento, a probabilidade de que dois
desenvolvedores selecionaram o mesmo nome foi baixo. No geral, para os 47 objetos que tiveram que ser
nomeados (ou seja, variáveis, constantes, estruturas de dados, funções e parâmetros combinados) a
probabilidade mediana de duas pessoas escolherem o mesmo nome era de apenas 7%.
Embora nomear seja difícil, escolher os nomes certos para objetos sobre os quais raciocinamos
no código é importante. Antes de mergulharmos na conexão entre nomeação e processos cognitivos no
cérebro, vamos ver por que nomear é importante.
Machine Translated by Google

Por que a nomenclatura é importante 129

8.1.1 Por que nomear é importante


Com nomes de identificadores, queremos dizer todas as coisas em uma base de código nomeada pelo
programador. Os nomes de identificador incluem nomes que atribuímos a um tipo (classe, interface,
struct, delegate ou enum), variável, método, função, módulo, biblioteca ou namespace. Há quatro razões
principais pelas quais os nomes dos identificadores são importantes.

OS NOMES CONSTITUEM UMA GRANDE PARTE DAS BASES DE CÓDIGO

A primeira razão pela qual os nomes de variáveis importam é que na maioria das bases de código
uma grande parte do que você lerá serão nomes. Por exemplo, no código-fonte do Eclipse, que
consiste em cerca de dois milhões de linhas de código, 33% dos tokens e 72% dos caracteres são
dedicados a identificadores.1

OS NOMES TÊM UM PAPEL NAS REVISÕES DE CÓDIGOS

Além de sua ocorrência frequente no código, os programadores falam muito sobre nomes. Miltiadis
Allamanis, agora pesquisador da Microsoft Research em Cambridge, investigou com que frequência
os nomes de identificadores são mencionados nas revisões de código. Para esse fim, Allamanis
analisou mais de 170 comentários com mais de 1.000 comentários neles. Ele descobriu que uma em
cada quatro revisões de código continha observações relacionadas à nomenclatura e que as
observações sobre nomes de identificadores ocorreram em 9%.
OS NOMES SÃO A FORMA DE DOCUMENTAÇÃO MAIS ACESSÍVEL

Embora a documentação formal do código possa conter muito mais informações básicas, os nomes
servem como um tipo importante de documentação porque estão disponíveis dentro da base de
código. Como vimos nos capítulos anteriores, reunir informações de diferentes lugares pode aumentar
a carga cognitiva. Como tal, ter que navegar fora de sua base de código para ler a documentação é
um ato que os programadores tentam evitar. Portanto, a “documentação” mais lida serão comentários
no código e nomes.

OS NOMES PODEM SERVIR COMO FARÓIS

Nos capítulos anteriores, discutimos beacons, partes do código que ajudam um leitor desconhecido a
desvendar o significado do código. Os nomes de variáveis são sinalizadores importantes que ajudam
os leitores a entender o código além dos comentários.

8.1.2 Diferentes perspectivas sobre nomenclatura

Escolher um bom nome é importante. Muitos pesquisadores diferentes tentaram definir o que torna
um nome de variável bom ou ruim, e todos eles têm perspectivas diferentes sobre essa questão. Mas
antes de olharmos para essas diferentes perspectivas, vamos ativar seu LTM e analisar sua opinião
sobre os nomes das variáveis com um exercício.

EXERCÍCIO 8.1 O que define um bom nome de identificador na sua opinião? Você consegue
pensar em um exemplo de um bom nome?
O que define um nome ruim? Isso é simplesmente o oposto de um bom nome, ou você
consegue pensar em propriedades que caracterizam nomes ruins que você já viu? Você
conhece um exemplo de um nome ruim de sua prática?

1
Florian Deißenbock e Markus Pizka, “Concise and Consistent Naming,” https://www.cqse.eu/fileadmin/
content/news/publications/2005-concise-and-consistent-naming.pdf.
Machine Translated by Google

130 CAPÍTULO 8 Como melhorar em nomear as coisas

Agora que você pensou sobre o que faz um nome ser um bom nome, vamos dar uma olhada em três
diferentes perspectivas sobre boas práticas de nomeação por pesquisadores que estudam nomeação.

UM BOM NOME PODE SER DEFINIDO SINTACTICAMENTE

Algumas pessoas acreditam que existem várias regras baseadas na sintaxe de nomes que devem ser
segure. Por exemplo, Simon Butler, professor associado sênior da Open University em
o Reino Unido, criou uma lista de problemas com nomes de variáveis, conforme mostrado na tabela 8.1.

Tabela 8.1 Lista de convenções de nomenclatura de Butler

Nome Descrição Exemplo de um nome ruim

Anomalia de maiúsculas Os identificadores devem usar letras maiúsculas apropriadas. contador de páginas

Consecutivo Os identificadores não devem conter vários page__counter


sublinha sublinhados consecutivos.

Palavras do dicionário Os identificadores devem consistir em palavras e pag_countr


use abreviações apenas quando forem mais
usadas do que as palavras completas.

Número de palavras Os identificadores devem ser compostos de page_counter_


duas a quatro palavras. convertido_e_
valor_normalizado

Palavras em excesso Os identificadores não devem ser compostos por page_counter_


mais de quatro palavras. convertido_e_
valor_normalizado

Nome de identificador curto Os identificadores não devem consistir em menos P, página


de oito caracteres, exceto c, d, e, g, i, in, inOut, j, k, m,
n, o, out, t, x, y, z.

Ordem de declaração do A menos que haja motivos claros para não fazê-lo, CardValue = {ACE, OITO,
identificador de enumeração os tipos de enumeração devem ser declarados em CINCO, QUATRO, JACK, REI...}
ordem alfabética.

Sublinhados externos Os identificadores não devem começar ou terminar __page_counter_


com sublinhado.

Codificação do identificador As informações de tipo não devem ser codificadas em int_page_counter


nomes de identificadores usando notação húngara ou
similar.

Nome de identificador longo Nomes de identificador longos devem ser evitados sempre page_counter_
que possível. convertido_e_
valor_normalizado

Anomalia de convenção de Os identificadores não devem combinar caracteres Page_counter


nomenclatura maiúsculos e minúsculos em caracteres não padrão
caminhos.

Identificador numérico Os identificadores não devem ser compostos CINQUENTA

nome inteiramente de palavras numéricas ou números.


Machine Translated by Google

Por que a nomenclatura é importante


131

Embora a lista de Butler contenha diferentes tipos de regras, a maioria é sintática. Por exemplo,
a regra “External underscores” afirma que os nomes não devem começar ou terminar com um

sublinhado. As regras de Butler também implicam na proibição dos sistemas de notação húngara, que
prescreveria nomes de variáveis como strName para uma variável que representa um nome
armazenado como uma string.

Embora as regras sobre a formação precisa de nomes de variáveis possam parecer um pouco mesquinhas,
nos capítulos anteriores vimos que informações desnecessárias no código podem causar carga cognitiva estranha e
distrair a compreensão do código, por isso é sensato também
têm regras sintáticas como as da tabela 8.1.
Muitas linguagens de programação, é claro, têm convenções sobre como formatar
nomes de variáveis, como PEP8 em Python, que prescreve o caso de cobra para nomes de variáveis,
e a convenção de nomenclatura Java, que afirma que os nomes das variáveis devem ser camel case.

OS NOMES DEVEM SER CONSISTENTES DENTRO DE UMA BASE DE CÓDIGO

Outra perspectiva sobre uma boa nomenclatura é a consistência. Allamanis, cujo trabalho em código
revisões e nomenclatura que abordamos no início do capítulo, também pensou em boas
nomes. Ele afirma que o aspecto mais importante de um bom esquema de nomenclatura é semelhante
execução em uma base de código.

A objeção contra práticas inconsistentes de nomenclatura se encaixa com o que sabemos sobre ciência
cognitiva. Se a mesma palavra for usada para objetos semelhantes em uma base de código, ela será
mais fácil para o cérebro encontrar informações relacionadas relevantes armazenadas no LTM. Simão

concorda parcialmente com a visão de Allamanis; sua lista contém uma regra dizendo que os nomes devem
não use letras maiúsculas de forma inconsistente.

EXERCÍCIO 8.2 Selecione um trecho de código no qual você trabalhou recentemente. Faça uma lista
de todos os nomes de variáveis que ocorrem nesse pedaço de código. Agora reflita sobre o
qualidade desses nomes, dadas as três perspectivas delineadas. são os nomes
sintaticamente claro? Eles consistem em palavras? Eles são usados de forma consistente em
sua base de código?

Nome Problemas sintáticos Consistente na base de código

8.1.3 As práticas iniciais de nomenclatura têm um impacto duradouro

Dawn Lawrie, pesquisadora sênior da Universidade Johns Hopkins que estudou


nomeação extensivamente, também investigou tendências em nomeação.2 As práticas de nomeação são diferentes
do que eram há uma década? E como os nomes mudam dentro de uma base de código
em períodos mais longos?
Machine Translated by Google

132 CAPÍTULO 8 Como melhorar em nomear as coisas

Para responder a essas perguntas, Lawrie analisou um total de 186 versões de 78 bases de código
escrito em C++, C, Fortran e Java. Juntas, essas versões tinham mais de 48 milhões de linhas
de código e durou três décadas. O conjunto analisado por Lawrie continha tanto código proprietário quanto projetos
de código aberto, incluindo bases de código conhecidas como Apache,
Eclipse, mysql e gcc e samba.
Para analisar a qualidade dos nomes de identificadores, Lawrie investiga dois aspectos das práticas de
nomenclatura. Primeiro, ela verificou se os nomes dividiam palavras dentro de nomes, por exemplo, usando
sublinhados entre as palavras ou usando maiúsculas. Lawrie argumenta que os nomes
que as palavras divididas são mais fáceis de entender. Em segundo lugar, ela olhou se as palavras
que ocorrem dentro de nomes de variáveis ocorrem no dicionário, seguindo a regra de Butler que
nomes devem consistir em palavras.
Como Lawrie estudou diferentes versões da mesma base de código ao longo do tempo, ela
poderia analisar como as práticas de nomenclatura mudam ao longo do tempo. Ela olhou como nomear
a qualidade mudou ao longo do tempo em todas as 78 bases de código combinadas e descobriu que o código
moderno usa identificadores que consistem em palavras de dicionário em nomes mais antigos
código, usando mais palavras do dicionário e mais comumente dividindo as palavras
dentro dos nomes das variáveis. Lawrie atribui essas práticas aprimoradas de nomenclatura ao amadurecimento da
programação como disciplina. O tamanho da base de código não mostrou correlação com
qualidade, então bases de código maiores não fazem melhor (ou pior) quando se trata de qualidade
de nomes de identificadores.

Lawrie não apenas comparou as bases de código mais antigas com as mais novas; ela também olhou
versões anteriores da mesma base de código para ver se as práticas de nomenclatura mudaram ao longo do tempo
dentro de uma base de código. Ela descobriu que dentro de uma única base de código, a nomenclatura não melhora
à medida que o código envelhece. Lawrie tira uma conclusão importante e acionável aqui, dizendo que “a qualidade
do identificador ocorre no início do desenvolvimento de um programa”. Então, quando você
iniciar um novo projeto, você pode querer ter um cuidado extra na escolha de bons nomes,
porque a maneira como você cria nomes nos estágios iniciais de um projeto provavelmente será
a forma como os nomes serão criados para sempre.
A pesquisa sobre o uso de testes no GitHub encontrou um fenômeno semelhante: novos contribuidores
para um repositório, muitas vezes analisam os testes existentes e os modificam em vez de ler o
diretrizes do projeto.3 Quando um repositório tem testes implementados, os novos contribuidores se sentem
obrigados a também adicionar testes e, como tal, cumprir a forma como o projeto está organizado.

DESCOBERTAS SOBRE PRÁTICAS DE NOMES AO LONGO DO TEMPO

ÿ O código moderno segue melhor as diretrizes de nomenclatura

ÿ Mas dentro de uma base de código, as práticas de nomenclatura permanecem constantes

ÿ Não há diferença entre bases de código menores e maiores em termos de nam


práticas de ing

2
Dawn Lawrie, Henry Field e David Binkley, “Quantifying Identifier Quality: AN. Análise de Tendências”,
http://www.cs.loyola.edu/~lawrie/papers/lawrieJese07.pdf.
3
Raphael Pham et al., “Criando uma compreensão compartilhada da cultura de teste em um site de codificação social”, 2013,
http://etc.leif.me/papers/Pham2013.pdf.
Machine Translated by Google

Aspectos cognitivos da nomeação 133

Até agora neste capítulo, vimos duas perspectivas diferentes sobre a nomenclatura, como mostrado em
tabela 8.2.

Tabela 8.2 Diferentes perspectivas sobre nomenclatura

investigador Perspectiva

Mordomo Nomes sintaticamente semelhantes

Allamanis Os nomes devem ser consistentes em uma base de código

A perspectiva de Butler é que podemos seguir várias diretrizes principalmente sintáticas para escolher
os nomes certos. Allamanis, por outro lado, não prescreve regras ou diretrizes fixas
na qualidade dos nomes, mas assume a posição de que a base de código deve liderar e
que consistente e ruim é melhor do que bom, mas inconsistente. Seria ótimo se houvesse
era uma maneira clara de nomear identificadores em código, mas o fato de que mesmo pesquisadores
opiniões variadas sublinha que o que é um bom nome está nos olhos de quem vê.

8.2 Aspectos cognitivos da nomeação


Agora que abordamos por que nomear é importante e quais são as diferentes perspectivas sobre nomeação,
vamos mergulhar na nomeação da perspectiva do que sabemos sobre cognição.

8.2.1 A formatação de nomes suporta seu STM


Dado o que sabemos sobre o processamento cognitivo do código no cérebro, podemos ver que
ambas as perspectivas fazem sentido do ponto de vista cognitivo, conforme descrito na tabela 8.3. Ter regras
claras sobre como formatar nomes de variáveis provavelmente ajudará seu STM a fazer
sentido dos nomes que você está lendo.

Tabela 8.3 Diferentes perspectivas sobre nomeação e sua conexão com a cognição

investigador Perspectiva Adapta-se à cognição porque

Allamanis Os nomes devem ser consistentes em Suporta fragmentação


uma base de código.

Mordomo Nomes sintaticamente semelhantes Menor carga cognitiva ao processar nomes

A abordagem de Allamanis, por exemplo, prescreve o uso de práticas de nomenclatura consistentes


através de uma base de código. Isso é sensato, pois é provável que suporte a segmentação. Se todos os nomes
foram formatados de maneiras diferentes, você teria que se esforçar em cada nome para
encontrar o seu significado.

A perspectiva de Butler também se encaixa com o que sabemos sobre processamento cognitivo. Ele
promove o uso de nomes que sejam sintaticamente semelhantes, por exemplo, não permitindo sublinhados
iniciais e usando maiúsculas consistentes. Nomes semelhantes também podem
diminuir sua carga cognitiva ao ler os nomes porque a informação relevante é
apresentado da mesma forma todas as vezes. Limite de Butler de quatro palavras em um identificador
Machine Translated by Google

134 CAPÍTULO 8 Como melhorar em nomear as coisas

name, embora aparentemente um pouco aleatório, se encaixa no limite da memória de trabalho, que
agora é estimada entre dois e seis pedaços.

MELHORE A CONSISTÊNCIA DE NOMES EM SUA BASE DE CÓDIGO Para melhorar


a consistência de nomes em bases de código, Allamanis implementou sua abordagem para
detectar nomes inconsistentes em uma ferramenta chamada Naturalize (http://
groups.inf.ed.ac.uk/naturalize/ ), que usa aprendizado de máquina para aprender bons
nomes (consistentes) de uma base de código e pode sugerir novos nomes para locais,
argumentos, campos, chamadas de método e tipos. Em um primeiro estudo, os autores do
Naturalize o usaram para gerar 18 pull requests em bases de código existentes com
sugestões para melhorar os nomes. Destes, 14 foram aceites, o que confere alguma
credibilidade ao seu sucesso. Infelizmente, em sua forma atual, o Naturalize funciona apenas para Java.

Em seu artigo sobre Naturalize, os autores compartilham uma linda história de quando
usaram o Naturalize para gerar um pull request para Junit. Este pull request não foi aceito,
pois de acordo com os desenvolvedores, a mudança proposta não era consistente com a
base de código. Naturalize poderia então indicá-los para todos os outros locais onde essa
convenção foi violada, causando a sugestão.
Sua própria convenção foi quebrada com tanta frequência que, para Naturalize, parecia
que a versão errada era mais natural!

8.2.2 Nomes claros ajudam seu LTM


As duas perspectivas de nomenclatura que vimos até agora são diferentes uma da outra, mas
compartilham algumas semelhanças. Ambos os métodos são sintáticos ou estatísticos, e um
programa de computador pode ser usado para medir a qualidade dos nomes de acordo com ambos
os modelos. Como vimos, o modelo da Allamadis também é implementado no software.

Memória de
Memória de
curto prazo
longo prazo
lista
Nome da variável de
Conhecimento
em em
name_ctr_list nomes nomes g, nomes
contadores , de
contadores e listas e
listas
SM Memória de
trabalho

name_ctr_list
Conhecimento
em em
nomes g, nomes
contadores , de
contadores e listas e
listas

Figura 8.1 Quando você lê um nome, o nome será primeiro dividido em


partes separadas e depois enviado para a memória de trabalho. Ao mesmo
tempo, o LTM é pesquisado por informações relacionadas às diferentes partes
do nome da variável. As informações relacionadas do LTM também são enviadas para a memória de trabalho.
Machine Translated by Google

Aspectos cognitivos da nomeação 135

Mas nomear, é claro, é mais do que selecionar a sintaxe correta para um nome de variável. O
as palavras que escolhemos também importam, especialmente de uma perspectiva cognitiva. Vimos anteriormente em
o livro que ao pensar em código, a memória de trabalho processa dois tipos de
em formação. Isso é ilustrado na figura 8.1. Primeiro, o nome da variável é processado por
memória sensorial e, em seguida, é enviado para o STM. Sabemos que o STM é limitado em tamanho
e, portanto, tenta fazer com que os nomes das variáveis separem as palavras. Quanto mais sistemática
nomes são formatados, mais provável é que o STM possa identificar partes individuais.
Por exemplo, em um nome como nmcntravg, pode ser necessário um esforço considerável para encontrar e
entender os componentes. Alternativamente, em um nome como name_counter_average é
muito mais fácil ver o que o nome diz respeito. Apesar de ter aproximadamente o dobro de personagens, requer uma
fração do esforço mental quando você o lê.
Ao processar nomes de variáveis, não é apenas o STM que fornece informações
para a memória de trabalho. A memória de trabalho também recebe informações do seu LTM
depois de ter sido pesquisado por fatos relacionados. Para este segundo processo cognitivo, a escolha
de palavras no nome do identificador é importante. Usar o conceito de domínio correto para um nome ou classe de
variável pode ajudar o leitor de código a encontrar informações relevantes em seu LTM.

8.2.3 Os nomes das variáveis podem conter diferentes tipos de informações


para ajudá-lo a entendê-las
Conforme descrito na figura 8.2, três tipos de conhecimento podem existir em nomes de identificadores e
pode ajudá-lo a entender rapidamente um nome desconhecido:

1 Os nomes podem apoiar seu pensamento sobre o domínio do código. Um domínio


palavras como “cliente” terão todos os tipos de associações em seu LTM. Um cliente
provavelmente está comprando um produto, precisa de um nome e um endereço e assim por diante.

2 O nome pode apoiar seu pensamento sobre programação. Um conceito de programação


como uma árvore também desbloqueará informações do LTM. Uma árvore tem uma raiz, pode ser
atravessado e achatado, e assim por diante.
3 Em alguns casos, a escolha de um nome de variável também contém informações sobre
convenções que seu LTM está ciente. Por exemplo, uma variável chamada j lembrará
você de um loop aninhado no qual j é o contador do loop mais interno.

Memória de longo prazo


Cliente Lista de árvores
eu

j
Domínio
Programação Convenções
conhecimento conceitos

n m
envio Mapa de hash

Figura 8.2 Três tipos de conhecimento armazenados em seu LTM podem ocorrer
em nomes de variáveis e podem ajudá-lo a entender os nomes: conhecimento de
domínio (como cliente ou remessa), conceitos de programação (como lista, árvore ou
mapa de hash) e convenções (por exemplo , i e j provavelmente serão contadores de loop e n e m serão dimensões).
Machine Translated by Google

136 CAPÍTULO 8 Como melhorar em nomear as coisas

Considerar como um nome de variável suportará tanto o STM quanto o LTM de um futuro leitor pode ser de grande
ajuda na escolha de nomes.

EXERCÍCIO 8.3 Selecione um trecho de código-fonte não muito familiar. Não deve ser
totalmente desconhecido, por exemplo, pode ser algum código em que você trabalhou há algum
tempo, ou um pedaço de código escrito por outra pessoa dentro de uma base de código em que
você também trabalha.

Percorra o código e anote uma lista de todos os nomes de identificadores no código: nomes de variáveis,
nomes de métodos e nomes de classes. Para cada um dos nomes, reflita sobre como o nome suporta seu
processamento cognitivo:

ÿ A formatação do nome é compatível com seu STM? O nome pode ser


melhorado para tornar as partes individuais mais claras?

ÿ O nome apoia seu LTM na compreensão do domínio? Pode o nome


ser melhorado para ser mais claro?

ÿ O nome suporta seu LTM na compreensão dos conceitos de programação


usava? O nome pode ser melhorado para ficar mais claro?

ÿ O nome suporta seu LTM no entendimento porque seu uso é baseado em


uma convenção de programação?

8.2.4 Quando avaliar a qualidade dos nomes


Vimos que nomear é difícil por causa dos processos cognitivos relacionados à codificação.
Quando você está engajado na solução de um problema, provavelmente está enfrentando uma alta carga cognitiva.
Talvez sua carga tenha sido tão alta ao resolver o problema que você não tinha mais nada para criar um bom nome de
variável. Acho que todos nós escrevemos código complexo no qual nomeamos uma variável foo porque não queríamos
pensar em nomear além de resolver o problema em questão. Além disso, talvez o significado da coisa que você estava
nomeando não tenha ficado claro até mais tarde no processo de programação.

Portanto, a codificação não é um bom momento para pensar em nomes e melhorar sua qualidade. É melhor refletir
sobre a qualidade de nomenclatura fora do processo de codificação.
As revisões de código podem ser um bom momento para refletir sobre a qualidade do código de seus nomes de
identificadores. O Exercício 8.4 pode servir como uma lista de verificação para nomes a serem usados, que você pode
usar em revisões de código para direcionar a atenção especificamente para nomes no código.

EXERCÍCIO 8.4 Antes de iniciar a revisão do código, liste mecanicamente todos os nomes de
identificadores presentes no código alterado. Liste esses nomes fora do código, por exemplo,
em um quadro branco ou em um documento separado. Para cada um dos nomes de identificador,
responda a estas perguntas:

ÿ Sem saber nada sobre o código, está claro o que o nome significa? Por exemplo, você sabe o
significado das palavras em que esse nome consiste?
ÿ Algum dos nomes é ambíguo ou pouco claro? ÿ Os
nomes usam abreviações que podem confundir? ÿ Quais nomes são
semelhantes? Esses nomes semelhantes também se referem a objetos semelhantes em
o código?
Machine Translated by Google

Que tipos de nomes são mais fáceis de entender? 137

8.3 Que tipos de nomes são mais fáceis de entender?


Até agora, investigamos por que bons nomes são importantes e exploramos a
impacto dos nomes nos processos cognitivos. Vamos agora ampliar opções mais detalhadas
sobre como formatar nomes de identificadores.

8.3.1 Abreviar ou não abreviar?


Até aqui, vimos a opinião de que os nomes devem ser criados combinando palavras do dicionário. Embora usar
palavras completas pareça uma escolha razoável, é bom mergulhar
a evidência que temos de que os identificadores que consistem em palavras completas são de fato mais fáceis de
Compreendo.

Johannes Hofmeister, pesquisador da Universidade de Passau, na Alemanha, realizou um experimento com


72 desenvolvedores profissionais de C# no qual os desenvolvedores
teve que encontrar bugs em trechos de código C#. Hofmeister estava interessado em saber se o significado ou a
forma dos nomes dos identificadores é mais importante para a descoberta de bugs bem-sucedida. Ele
apresentou aos desenvolvedores três tipos diferentes de programas: um programa onde
identificadores eram letras, um segundo programa em que os identificadores eram abreviações,
e finalmente um programa em que os identificadores eram palavras. Hofmeister pediu aos participantes que
encontrassem erros de sintaxe e semântica. Ele então mediu o tempo que levou para
os participantes para encontrar bugs nos programas fornecidos.
Os participantes, em média, encontraram 19% mais defeitos por minuto ao ler programas em que os
identificadores eram palavras em comparação com letras e abreviações.
Não houve diferença significativa na velocidade entre letras e abreviaturas.
Enquanto outros estudos confirmam que variáveis constituídas por palavras auxiliam na compreensão,
também pode haver desvantagens em usar nomes de variáveis mais longos.4 Lawrie, cujo trabalho
abordado anteriormente neste capítulo, realizou um estudo no qual 120 desenvolvedores profissionais com
uma média de 7,5 anos de experiência profissional foram solicitados a compreender e
lembre-se do código-fonte com os mesmos três tipos diferentes de identificadores: palavras,
abreviaturas ou letras simples.
Foi mostrado aos participantes do estudo um método usando um dos três identificadores
estilos. O código foi então removido da visualização, após o que os participantes foram solicitados a
explicar o código em palavras e depois lembrar os nomes das variáveis que ocorreram no programa. Ao contrário
de Hofmeister, Lawrie mediu quão bem os resumos do código
correspondeu à funcionalidade real do código, classificando as respostas dos participantes em uma escala de 1 a
5.
Lawrie encontrou resultados como o de Hofmeister: identificadores compostos por palavras são mais fáceis de
compreender do que ambas as outras categorias. Resumos de código usando identificadores de palavras
foram classificados quase um ponto a mais pelos pesquisadores do que os resumos de código usando
identificadores de uma única letra.

4
Dawn Lawrie et al., “Effective Identifier Names for Comprehension and Memory”, 2007, https://www.research
gate.net/publication/220245890_Effective_identifier_names_for_comprehension_and_memory.
Machine Translated by Google

138 CAPÍTULO 8 Como melhorar em nomear as coisas

O estudo também revelou uma desvantagem de usar identificadores de palavras. Ao investigar


os resultados da atribuição de recall, Lawrie descobriu que nomes de variáveis mais longos são
mais difícil de lembrar, e leva mais tempo para lembrá-los. Não é o comprimento por
se que torna os nomes de variáveis mais difíceis de lembrar, mas o número de sílabas que o
nomes contêm. É claro que isso é compreensível de uma perspectiva cognitiva: mais tempo
nomes podem usar mais pedaços no STM, e sílabas são um método provável para separar palavras. Assim,
escolher um bom nome de variável é um equilíbrio cuidadoso entre clareza de
palavras, o que melhorará a capacidade do leitor de entender o código e encontrar bugs, e a brevidade das
abreviaturas, o que pode melhorar a lembrança de nomes.
Com base em seu estudo, Lawrie aconselha ter cuidado com o uso de convenções de nomenclatura que
envolvem prefixar ou sufixar um identificador. Essas práticas devem ser cuidadosamente avaliadas para garantir
que as informações adicionadas superem o custo adicional do
nomes difíceis de lembrar.

CUIDADO COM PREFIXOS E SUFIXOS Lawrie aconselha ter cuidado com o uso
de convenções de nomenclatura que envolvem prefixar ou sufixar um identificador.

LETRAS ÚNICAS SÃO COMUMENTE USADAS COMO VARIÁVEIS


Até agora, vimos que as palavras são melhores identificadores do que abreviaturas ou letras,
tanto em termos de encontrar bugs mais rapidamente quanto em termos de melhor compreensão.
Letras simples, no entanto, são comumente usadas na prática. Gal Beniamini, pesquisadora da
Universidade Hebraica de Jerusalém, estudou com que frequência letras simples são usadas em C,
Java, JavaScript, PHP e Perl. Para cada uma dessas cinco linguagens de programação,
Beniamini baixou os 200 projetos mais populares do GitHub, junto com
mais de 16 GB de código-fonte.

Os resultados de Beniamini mostraram que diferentes linguagens de programação têm convenções bastante
diferentes para o uso de nomes de variáveis de uma única letra. Por exemplo, para Perl,
os três nomes de uma única letra mais usados são, em ordem, v, i e j, enquanto para JavaScript
os nomes de uma única letra mais comuns são i, e e d. A Figura 8.3 mostra o uso de todos os 26
letras nas cinco linguagens de programação diferentes Beniamini analisadas.
Beniamini não olhou apenas para a ocorrência de nomes de variáveis de uma única letra; ele
também estava interessado nas associações que os programadores têm para as letras. Para uma letra como eu,
a maioria dos programadores pensará em um iterador de loop, e x e y provavelmente serão coordenadas
em um avião, mas e as outras letras, como b, f, s ou t? Essas letras estão associadas a um significado comum
por muitos programadores? Conhecer as suposições que as pessoas fazem sobre nomes de variáveis pode
ajudá-lo a evitar equívocos e
também ajudam você a entender as maneiras como os outros estão confusos sobre o código.
Para obter uma compreensão dos tipos que os programadores associam às variáveis
nomes, Beniamini fez uma pesquisa com 96 programadores experientes, onde perguntou
para listar um ou mais tipos aos quais eles associariam variáveis de uma única letra. Como você puder
veja na figura 8.4, há pouco consenso sobre os tipos da maioria das letras. Exceções notáveis
são s, que é majoritariamente votado para ser uma string, c, que é majoritariamente um caractere, e i, j, k e n,
que são inteiros. Mas para todas as outras cartas, quase tudo vale.
Machine Translated by Google

Que tipos de nomes são mais fáceis de entender? 139

Perl Javascript

aBCDeFGHIJKLMNopqRSTUVWxyZ aBCDeFGHIJKLMNopqRSTUVWxyZ

PHP C

aBCDeFGHIJKLMNopqRSTUVWxyZ aBCDeFGHIJKLMNopqRSTUVWxyZ

Java

Figura 8.3 Variáveis de uma única letra


usadas em cinco linguagens de programação
aBCDeFGHIJKLMNopqRSTUVWxyZ diferentes que Beniamini analisou

outro
flutuador
boleano

90 array
char
Respostas 80 string
inteiro
70

60

50

40

30

20

10

0
aBCDeFGHIJKLMNopqRSTUVWxyZ
Figura 8.4 Tipos associados a variáveis de uma única letra
Machine Translated by Google

140 CAPÍTULO 8 Como melhorar em nomear as coisas

Surpreendentemente, d, e, f, r e t tendem a ser associados a números de ponto flutuante, e as variáveis x, y e


z são tão fortemente associadas a números inteiros quanto a números inteiros.
números de ponto flutuante, o que pode significar que, quando usados como coordenadas, eles são
usado tanto em locais onde as coordenadas são inteiros quanto em locais onde elas são
Números de ponto flutuante.
Os resultados de Beniamini sobre associações de tipo para nomes de variáveis de uma letra principalmente
lembre-nos de que não podemos tomar como certas as suposições dos outros. Podemos pensar que
certa carta certamente transmitirá a ideia de um determinado tipo, ajudando o leitor a
sentido do código, mas isso é improvável, com exceção de alguns casos específicos. Portanto,
escolher palavras como nomes ou concordar com convenções é uma aposta melhor para código futuro
compreensão.

EXERCÍCIO 8.5 Escreva os tipos que você esperaria para todas as 26 letras
nomes de variáveis na tabela a seguir. Em seguida, compare as notas com sua equipe
membros. Existem cartas em que suas suposições diferem? Você pode encontrar
lugares em sua base de código onde essas letras são usadas como variáveis?

Carta Modelo Carta Modelo Carta Modelo

aj s
bk t

cl u

dmv

en w

Raposa

gp y

hq z
eu r

8.3.2 Caixa de cobra ou caixa de camelo?


Embora a maioria das linguagens de programação tenha um guia de estilo que também descreve como devem
ser os nomes das variáveis, nem todos os guias de estilo concordam. Famosamente, as linguagens da família C,
incluindo C, C++, C# e Java, todos usam camel case onde a primeira letra de qualquer variável
é minúscula, e cada nova palavra dentro do nome começa com uma letra maiúscula, por
exemplo, customerPrice ou nameLength. Python, por outro lado, usa uma convenção chamada snake case, que
separa palavras em nomes de identificadores com sublinhados, por
exemplo, customer_price ou name_length.
Dave Binkley, professor de ciência da computação da Loyola University em Maryland, realizou um estudo
investigando as diferenças de compreensão entre variáveis escritas em camel case e aquelas escritas em snake
case.5 Binkley queria saber se

5
Dave Binkley, “To Camel Case or Under_Score”, 2009, https://ieeexplore.ieee.org/abstract/document/
5090039.
Machine Translated by Google

A influência dos nomes nos bugs 141

os dois estilos de identificador afetam a velocidade e a precisão com que as pessoas podem se adaptar
programas. No estudo de Binkley, participaram 135 pessoas, tanto programadores quanto não programadores.
Os participantes do estudo viram primeiro uma frase descrevendo o
variável, por exemplo, “Estende uma lista para uma tabela”. Após o estudo da sentença, os participantes
receberam quatro opções de múltipla escolha, das quais uma representava a sentença. Exemplos de opções
que os participantes podem escolher são
extendListAsTable, expandAliasTable, expandAliasTitle ou expandAliasTable.
Os resultados do estudo de Binkley mostram que o uso do camel case leva a uma maior precisão entre
programadores e não programadores. O modelo descobre que existe
uma chance 51,5% maior de selecionar a opção certa para identificadores escritos no
estilo de caixa de camelo. Mas há um custo para essa maior precisão: velocidade. Levou os participantes
meio segundo a mais para encontrar identificadores escritos em caixa de camelo.

Além de comparar os dois estilos de identificadores diferentes e observar o


resultados de programadores e não programadores, Binkley também analisou o efeito
do ensino de programação sobre o desempenho do assunto, comparando pessoas sem
treinamento para pessoas com mais anos de treinamento. Os participantes do estudo de Binkley que
receberam treinamento foram principalmente treinados usando caso de camelo.
Ao comparar pessoas com diferentes níveis de experiência, Binkley descobriu que os programadores com
mais treinamento em caso de camelo eram mais rápidos em encontrar os identificadores certos escritos no
estilo de caso de camelo. O treinamento em um estilo de identificador parece
impactar o desempenho de uma pessoa no uso de outros estilos. Os resultados de Binkley demonstraram
que os sujeitos com mais treinamento no caso do camelo eram mais lentos nos identificadores escritos em
caso de cobra do que sujeitos sem nenhum treinamento.
Sabendo o que sabemos sobre processamento cognitivo, esses resultados são um pouco menos
surpreendentes do que podem parecer. Se as pessoas praticam muito o uso de nomes em maiúsculas e minúsculas, elas
ficar melhor em pedaços de nomes e encontrar significado neles.
Claro, se você estiver trabalhando em uma base de código existente que usa snake case, seria
ser imprudente mudar todos os nomes de variáveis de acordo com este estudo. A consistência é uma
aspecto importante também. No entanto, se você se encontrar na posição de decidir sobre
uma convenção de nomenclatura, você pode optar pelo caso camel.

8.4 A influência dos nomes nos bugs


Neste capítulo até agora, vimos por que nomear é importante e que tipos de nomes
são mais fáceis de entender. No entanto, as más práticas de nomenclatura também podem ter um efeito direto
impacto na ocorrência de bugs.

8.4.1 Código com nomes ruins tem mais bugs


Simon Butler, cujo trabalho sobre diretrizes de nomenclatura abordamos anteriormente neste capítulo,
também analisou a relação entre nomes ruins e bugs. Butler realizou um estudo
em 20096 que investigou a relação entre nomes incorretos e códigos incorretos investigando repositórios de
código aberto escritos em Java, incluindo Tomcat e Hibernate.

6
Simon Butler, “Relating Identifier Naming Flaws and Code Quality: An Empirical Study”, 2009, http://
oro.open.ac.uk/17007/1/butler09wcreshort_latest.pdf.
Machine Translated by Google

142 CAPÍTULO 8 Como melhorar em nomear as coisas

Butler criou uma ferramenta para extrair nomes de variáveis do código Java e também detectar violações das
diretrizes de nomenclatura. Usando sua ferramenta, Butler localizou lugares na caixa oito
bases onde ocorreram estilos de nomenclatura ruins. Conforme explicado na seção 8.1, Butler procurou
tanto em questões estruturais de nomenclatura, como dois sublinhados consecutivos, quanto na
componentes dos nomes, como se eles ocorrem em um dicionário.
Butler então comparou os locais no código com um estilo de nomenclatura ruim com o
localizações de bugs, como encontrado com FindBugs, uma ferramenta que localiza possíveis locais de bugs
usando análise estática. Curiosamente, o estudo de Butler encontrou associações estatisticamente significativas
entre problemas de nomenclatura e qualidade do código. As descobertas de Butler sugerem que um mau
estilo de nomenclatura pode apontar para código que provavelmente está errado em oposição ao código que é
apenas difícil de ler, entender e manter.
Claro, a correlação entre os locais dos bugs e os locais dos nomes ruins
não implica necessariamente causalidade. Pode ser que tanto os bugs quanto os nomes ruins sejam
o resultado do código escrito por um programador novato ou desleixado. Também pode ser o caso
que o local onde os bugs ocorrem é onde os problemas complexos estão sendo resolvidos.
Esses problemas complexos podem estar relacionados aos erros de nomenclatura de outras maneiras. Como já
discutimos antes, talvez a carga cognitiva do programador tenha sido muito alta ao criar o código porque eles
estavam resolvendo um problema difícil. Pode ser também o caso de
o domínio do código é complexo e, portanto, criar um bom nome é
difícil, e a complexidade de pegar o nome certo levou à confusão e, portanto, a um bug.
Portanto, embora resolver problemas de nomenclatura não resolva ou impeça necessariamente
bugs, inspecionando sua base de código para encontrar locais onde ocorrem práticas ruins de nomenclatura
pode ajudá-lo a encontrar lugares onde o código pode ser melhorado e os bugs podem ser evitados. Essa é uma
razão extra para ir à caça de nomes ruins no código: melhorar
nomes podem resultar indiretamente em menos bugs, ou pelo menos tempos de correção mais curtos porque melhor
nomes tornarão o código mais fácil de compreender.

8.5 Como escolher nomes melhores


Vimos que os efeitos da má nomeação são graves, podem levar a menores chances de
compreensão do código, e pode até aumentar a chance de bugs. Feitelson, cujo
trabalhar na escolha de nomes que abordamos anteriormente, também estudamos como os desenvolvedores podem selecionar
nomes melhores.7

8.5.1 Moldes de nome


Em sua pesquisa em que os desenvolvedores foram solicitados a selecionar nomes de variáveis, Feitelson viu que
mesmo que os desenvolvedores raramente selecionassem o mesmo nome para variáveis, eles poderiam
para entender nomes escolhidos por outros desenvolvedores. Quando um nome específico foi escolhido em
O experimento de Feitelson, era normalmente entendido pela maioria dos desenvolvedores. Uma razão para isso
aparente contradição é que os desenvolvedores usaram o que Feitelson chama de moldes de nomes.
Moldes de nome são padrões nos quais os elementos em um nome de variável são normalmente combinados.
Por exemplo, quando um nome era necessário para os benefícios máximos que alguém pode

7
Dror G. Fietelson, “Como os desenvolvedores escolhem nomes”, https://www.cs.huji.ac.il/~feit/papers/Names20TSE.pdf.
Machine Translated by Google

Como escolher nomes melhores 143

receber por mês, os nomes da tabela 8.4 foram todos escolhidos. Os nomes também são normalizados,
então “max” pode ser “max” ou “maximum” e “benefício” pode ser “benefícios”.
A tabela lista os nomes em ordem do mais para o menos escolhido.

Tabela 8.4 Formas mais para menos escolhidas de nomes de variáveis

max_benefit

max_benefit_per_month

max_benefit_num

max_monthly_benefit

benefícios

benefícios_por_mês

max_num_of_benefit

max_month_benefit

núm_máximo_benefício

max_number_of_benefit

max_benefit_amount

max_acc_benefit

max_allowed_benefit

limite_benefício_mensal

Observar esses moldes nos ajuda a entender por que a chance de dois desenvolvedores escolherem o
mesmo nome de variável no estudo de Feitenson era tão baixa. A infinidade de nomes diferentes no
experimento foi principalmente de desenvolvedores usando diferentes moldes de nomes.
Embora todos esses nomes representem conceitualmente o mesmo valor, há muitas diferenças de
estilo. Os desenvolvedores no estudo de Feitelson não trabalharam todos na mesma base de código, mas
mesmo dentro da mesma base de código é provável que esses diferentes moldes ocorram.
Saber o que sabemos agora sobre carga cognitiva e LTM, usando diferentes moldes dentro de uma base
de código não é uma boa ideia.
Primeiro, em termos de carga cognitiva, procurar o conceito relevante no nome da variável (neste
caso, benefício) e em locais diferentes dentro do nome da variável adiciona carga cognitiva desnecessária
e estranha. A energia mental que você gastará procurando o conceito certo não pode ser gasta na
compreensão dos nomes. Anteriormente neste capítulo, vimos que as pessoas podem ser treinadas para
reconhecer variáveis em um determinado estilo, como camel case ou snake case. Embora nenhum estudo
tenha sido feito sobre moldes de nomes, as pessoas provavelmente ficarão melhores em reconhecer
variáveis escritas em um determinado molde quando as usarem com frequência.

Em segundo lugar, se os nomes das variáveis forem semelhantes, usar o mesmo molde provavelmente
tornará mais fácil para o seu LTM encontrar informações relacionadas. Por exemplo, max_benefit_amount
Machine Translated by Google

144 CAPÍTULO 8 Como melhorar em nomear as coisas

pode lembrá-lo do código que você escreveu antes para calcular o valor máximo de juros se essa
variável tiver o nome de max_interest_amount. Seu LTM terá mais dificuldade em lembrar um código
semelhante quando a variável envolvida for chamada de interest_maximum, mesmo que o cálculo seja
semelhante.
Como moldes semelhantes suportam melhor sua memória de trabalho e seu LTM, é aconselhável
concordar com um número limitado de moldes diferentes para usar em cada base de código.
Quando você inicia um projeto, concordar com os moldes pode ser um bom passo nessa direção.
Em uma base de código existente, você pode começar criando ou extraindo uma lista de nomes de
variáveis existentes para a base de código, ver quais moldes já estão em uso e decidir sobre esses
moldes daqui para frente.

EXERCÍCIO 8.6 Crie uma lista de nomes de variáveis e funções/métodos em parte de sua
base de código. Isso pode ser uma classe, todo o código em um arquivo ou todo o código
relacionado a um determinado recurso. Para cada um dos nomes, verifique qual molde eles
seguem usando a tabela a seguir. Nos nomes da tabela, X representa uma quantidade ou
valor como juros de IVA e Y representa um determinado filtro na quantidade, como por mês,
para um determinado cliente.

Discuta os resultados com sua equipe. Quais moldes são comumente usados? Algumas
variáveis podem ser escritas usando um molde diferente para alcançar mais consistência
em sua base de código?

Mofo Variável Função/método

max_X

max_X_per_Y

max_X_num

X_per_Y

max_num_of_X

max_Y_X

X_max_num

max_number_of_X

max_X_amount

max_acc_X

max_allowed_X

Y_X_limit

max_X

De outros
Machine Translated by Google

Como escolher nomes melhores 145

8.5.2 Modelo de três etapas de Feitelson para melhores nomes de variáveis

Vimos que os programadores costumam usar muitos moldes de nomes diferentes para os mesmos objetos,
enquanto o uso de moldes semelhantes pode ajudar na compreensão. Com base nessas descobertas,
Feitelson projetou um modelo de três etapas para ajudar os desenvolvedores a escolher nomes melhores:

1 Selecione os conceitos a serem incluídos no nome.


2 Escolha as palavras para representar cada conceito.
3 Construa um nome usando essas palavras.
O MODELO DE TRÊS ETAPAS EM DETALHE

Vamos explorar essas três etapas com mais detalhes. A primeira etapa, selecionar os conceitos a serem
incluídos no nome, é muito específica do domínio, e a decisão sobre quais dimensões incluir pode ser a
decisão mais importante na nomenclatura. O principal a se levar em consideração na hora de escolher quais
partes incluir no nome é, segundo Feitelson, a intenção do nome, que deve representar quais informações o
objeto contém e para que serve. Quando você sentir a necessidade de escrever um comentário para explicar
o nome, ou quando encontrar um comentário próximo a um nome no código que você usa, as palavras do
comentário provavelmente devem ser incluídas no nome da variável. Em alguns casos, pode ser importante
incluir também uma indicação de que tipo de informação se trata, por exemplo, que um comprimento está na
dimensão horizontal ou vertical, que um peso está armazenado em quilos ou que um buffer contém entrada
do usuário e, portanto, deve ser considerado inseguro. Às vezes, podemos até usar um novo nome quando
os dados são convertidos.

Por exemplo, após a validação da entrada, ela pode ser armazenada em outra variável com um nome
indicando que é segura.
O segundo passo do modelo de Feitelson é escolher as palavras para representar cada conceito. Muitas
vezes, escolher as palavras certas é simples, com uma palavra específica sendo a escolha óbvia porque é
usada no domínio do código ou foi usada em

a base de código. No entanto, em seus experimentos, Feitelson observou que também havia muitos casos
em que, para pelo menos uma das palavras, muitas opções diferentes eram sugeridas pelos participantes.
Essa diversidade pode causar problemas quando os desenvolvedores ficam confusos sobre se sinônimos
significam a mesma coisa ou representam diferenças sutis. Um léxico de projeto, no qual todas as definições
importantes são anotadas e as alternativas para sinônimos são registradas, pode ajudar os programadores
a selecionar nomes de forma consistente.
Feitelson observa que as etapas de seu modelo não precisam necessariamente ser executadas em
ordem. Às vezes você pode pensar em palavras para usar em um nome de variável sem considerar os
conceitos que elas representam. Nesses casos, é importante ainda considerar os conceitos.

A terceira etapa do modelo de Feitelson é construir um nome com as palavras escolhidas, que se
resume a selecionar um dos moldes de nomeação. Como explicamos, ao escolher um molde, o alinhamento
com sua base de código pode ser importante. Nomes consistentes tornarão mais fácil para os outros localizar
os elementos importantes dentro do nome e relacionar o nome a outros nomes. Uma segunda consideração
que Feitelson aconselha é usar

moldes para que se ajustem à linguagem natural em que as variáveis são definidas. Por exemplo,
Machine Translated by Google

146 CAPÍTULO 8 Como melhorar em nomear as coisas

em uma frase em inglês, você diria “o número máximo de pontos” em vez de “o máximo de pontos”. Portanto,
você pode preferir max_points sobre points_max.
Outra maneira de fazer com que nomes de variáveis pareçam mais naturais é adicionar uma preposição, como
como em indexOf ou elementAt.

O SUCESSO DO MODELO DE TRÊS ETAPAS DE FEITELSON

Depois de definir o modelo de três etapas, Feitelson realizou um segundo experimento com 100 novos
participantes.
Os pesquisadores explicaram o modelo aos novos participantes, que receberam um exemplo. Após a
explicação, os participantes receberam os mesmos nomes dos participantes do estudo original de Feitelson.
Dois juízes externos foram então solicitados a comparar pares de dois nomes: um do primeiro estudo, onde os
participantes não conheciam o modelo, e um decorrente do segundo experimento, no qual os participantes
foram treinados no uso do modelo. Os juízes não sabiam qual nome veio de qual estudo.

As escolhas dos juízes mostraram que os nomes selecionados pelos sujeitos usando o modelo foram
vistos como superiores aos nomes escolhidos no experimento original na proporção de dois para um.
Assim, usar essas três etapas leva a nomes melhores.

Resumo
ÿ Existem diferentes perspectivas sobre o que faz um bom nome, desde regras sintáticas,
como o uso de camel case, até ênfase na consistência dentro de uma base de código.

ÿ Sem outras diferenças, as variáveis camel case são mais fáceis de lembrar do que as
variáveis escritas em snake case, mas as pessoas são mais rápidas em identificar variáveis
ao usar snake case. ÿ Locais no código onde ocorrem nomes incorretos também são mais
propensos a conter bugs,
mas isso não significa necessariamente que haja uma causação.
ÿ Existem muitos moldes de nomes diferentes usados para moldar nomes de variáveis, mas
limitar-se a um número menor de moldes provavelmente ajudará na compreensão. ÿ A
aplicação do modelo de três etapas de Feitelson (quais conceitos usar em um nome, quais
palavras usar para esses conceitos e como combiná-los) leva a nomes de maior qualidade.
Machine Translated by Google

Evitando código ruim


e carga cognitiva:
Duas estruturas

Este capítulo abrange


ÿ Explicando a conexão entre cheiros de código
e processos cognitivos, especialmente carga cognitiva
ÿ Pesquisando a conexão entre nomes ruins e carga
cognitiva

Como programador profissional, tenho certeza de que você já viu código fácil de ler,
assim como o código que você teve que gastar muito esforço para entender. O código de razão pode
ser difícil de entender é algo que abordamos nos capítulos anteriores ao discutir
STM, LTM e memória de trabalho: você está experimentando muita carga cognitiva.
A carga cognitiva acontece quando sua memória de trabalho fica muito cheia e seu
cérebro não pode processar corretamente mais. Nos capítulos anteriores, focamos em como
leia o código. Vimos que às vezes você precisa obter mais conhecimento de syn tax, conceitos ou
conhecimento de domínio para ler o código com maior facilidade.

147
Machine Translated by Google

148 CAPÍTULO 9 Evitando código ruim e carga cognitiva: dois frameworks

Neste capítulo, exploraremos o que se sabe sobre escrever código a partir de um


perspectiva. Veremos que tipo de código causa uma alta carga cognitiva e como
melhorar o código para que seja mais fácil de processar. Em particular, vamos mergulhar em duas razões diferentes
pelas quais o código pode causar carga cognitiva. Primeiro, o código pode ser difícil de entender porque é
estruturalmente confuso e, segundo, pode ser confuso porque seu conteúdo é confuso. Ao examinar o que torna o
código difícil de ler, você pode aprender a escrever código que
é mais fácil de entender e manter, o que significa que os membros da sua equipe
(incluindo você no futuro) precisará de menos esforço para ler e adotar o código e o risco de
bugs serão menores.

9.1 Por que código com cheiro de código cria muita carga cognitiva
Neste capítulo, examinamos como escrever código que não seja confuso para os outros; em outros
palavras, código que não impõe muita carga cognitiva para o leitor. O primeiro trabalho de estrutura que usamos para
estudar por que o código pode ser confuso é a ideia de cheiros de código: partes do código
que não estão estruturados idealmente. (Os cheiros de código foram cunhados por Martin Fowler em seu 1999
livro Refatoração: Melhorando o design do código existente [Addison-Wesley Professional].)
Exemplos de cheiros de código são métodos muito longos ou instruções switch excessivamente complexas.
Você já deve estar familiarizado com os cheiros de código. Se não estiver, a próxima subseção
fornece uma breve visão geral. Após a breve recapitulação dos cheiros de código, mergulhamos nos cheiros de código
e sua conexão com os processos cognitivos, especialmente a carga cognitiva. Simplesmente dizendo
“Essa classe é muito grande” é útil, mas às vezes não é útil o suficiente porque nós
querem entender exatamente quão grande é “grande demais” e de quais fatores isso depende.

9.1.1 Uma breve introdução aos cheiros de código

Fowler descreve um catálogo de diferentes cheiros de código, combinados com estratégias para aliviar
cheiros de código, que ele chamou de refatorações. Exemplos de cheiros de código são métodos muito longos,
classes que tentam fazer muito ao mesmo tempo e instruções de comutação excessivamente complexas. Como
vimos anteriormente neste livro, o termo “refactoring” tornou-se
um pouco separado dos cheiros de código em que uma refatoração agora também pode indicar um
melhoria para codificar em um sentido mais genérico, além de aliviar um cheiro de código. Por
Por exemplo, a mudança de um loop em uma compreensão de lista é vista pela maioria das pessoas como uma
refatoração, mesmo que um loop não contenha necessariamente um cheiro de código.
O livro de Fowler descreve um catálogo de 22 cheiros de código, que resumimos na tabela
9.1. Embora Fowler não faça essa distinção em seu livro, os 22 cheiros de código podem ser
dividido em diferentes níveis, também indicados na tabela 9.1 Alguns code smells dizem respeito a um

método único, como o método longo, enquanto outros pertencem a uma base de código inteira, como
comentários. A próxima subseção mergulha no cheiro de código em cada um dos níveis.
Machine Translated by Google

Por que o código com cheiro de código cria muita carga cognitiva 149

Tabela 9.1 Visão geral dos cheiros de Fowler e os níveis aos quais eles pertencem

Cheiro de código Explicação Nível

Método longo Um método não deve consistir em muitas linhas de código realizando cálculos diferentes. Nível do método

Longa lista de parâmetros Um método não deve ter muitos parâmetros. Nível do método

Alternar declarações O código não deve conter instruções switch grandes; polimorfismo poderia ser usado para Nível do método

facilitar o código.

Classes alternativas com Não deve haver duas classes diferentes que pareçam diferentes à primeira vista, mas que Nível da turma
interfaces diferentes tenham campos e métodos semelhantes.

Obsessão primitiva Evite o uso excessivo de tipos primitivos em uma classe. Nível da turma

Classe de biblioteca Os métodos não devem ser adicionados a classes aleatórias em vez de a uma classe de Nível da turma
incompleta biblioteca.

Turma grande Uma classe não deve ter muitos métodos e campos, deixando claro qual abstração a classe Nível da turma

fornece.

Classe preguiçosa Uma classe não deveria estar fazendo muito pouco para justificar sua existência. Nível da turma

Classe de dados As classes não devem conter apenas dados; eles devem conter métodos também. Nível da turma

Campo temporário As classes não devem conter campos temporários desnecessários. Nível da turma

Agrupamentos de dados Os dados frequentemente usados em combinação pertencem juntos e devem ser Nível da turma

armazenados juntos em uma classe ou estrutura.

Mudança divergente Geralmente, as alterações de código devem ser locais, de preferência para uma classe. Nível de base de código

Se você tiver que fazer muitas alterações diferentes em lugares diferentes, isso indica uma
estrutura ruim no código.

Inveja do recurso Quando muitos métodos da classe A são referenciados da classe B, eles pertencem a B e Nível de base de código

devem ser movidos para lá.

Intimidade inadequada As aulas não devem ser conectadas extensivamente a outras classes. Nível de base de código

Código duplicado ou clones O mesmo código ou código muito semelhante não deve ocorrer em vários locais diferentes Nível de base de código

de código em uma base de código.

Comentários Os comentários devem descrever por que o código está lá, não o que ele faz. Nível de base de código

Cadeias de mensagens Evite cadeias de mensagens que são longas cadeias de chamadas de mensagens, Nível de base de código

onde métodos chamam métodos chamam métodos e


em breve.

homem intermediário Se uma classe está delegando muita responsabilidade, ela deveria existir? Nível de base de código

Herança paralela Quando você faz uma subclasse de uma classe, você precisa fazer uma subclasse de outra. Nível de base de código

Isso indica que a funcionalidade de ambas as classes pode pertencer a uma classe.
Machine Translated by Google

150 CAPÍTULO 9 Evitando código ruim e carga cognitiva: dois frameworks

Tabela 9.1 Visão geral dos cheiros de Fowler e os níveis aos quais eles pertencem (continuação)

Cheiro de código Explicação Nível

Legado recusado Quando as classes herdam um comportamento que não usam, a herança pode não Nível de base de código

ser necessária.

Cirurgia de espingarda Geralmente, as alterações de código devem ser locais para uma classe. Se você tiver Nível de base de código

que fazer muitas alterações diferentes em lugares diferentes, isso indica uma estrutura
ruim no código.

Generalidade especulativa Não adicione código a uma base de código “apenas por precaução”; apenas adicione recursos que Nível de base de código

são necessários.

CHEIROS DE CÓDIGO DE NÍVEL DE MÉTODO

Um exemplo de cheiro de código que pertence a um método individual é um método que


consiste em muitas linhas de código e tem muitas funcionalidades. Diz-se que tal método
sofrem com o cheiro do método longo ou do método de Deus. Outro cheiro de código ocorre quando um
método tem um grande número de muitos parâmetros. Segundo Fowler, tal método
sofre com o cheiro de muitos parâmetros.
Para leitores não familiarizados com o conceito, é aconselhável ler o livro de Fowler. Mesa
9.1 fornece uma visão geral dos 22 cheiros de código de Fowler, incluindo o nível em que ocorrem.

CHEIROS DE CÓDIGO DE NÍVEL DE CLASSE

Além dos cheiros de código que existem no nível do método, existem cheiros de código no

nível de classe. Um exemplo é uma classe grande, às vezes também chamada de classe de Deus. Uma grande classe é
uma classe que tem tanta funcionalidade que não é mais uma abstração significativa. Deus
as classes normalmente não são criadas de uma só vez, mas ocorrem ao longo do tempo. Primeiro você pode criar um
classe para lidar com a exibição da conta de um cliente, que pode conter métodos relacionados
para marcar bem as informações do cliente, como print_name_and_title() ou
show_date_of_birth(). Lentamente, a funcionalidade da classe é expandida com alguns
método que também realiza alguns cálculos simples como determine_age(). Hora extra,
são adicionados métodos que não consideram um cliente individual, mas também podem listar todos

clientes de um determinado representante, e assim por diante. Em algum momento, a classe não mais
representa a lógica relacionada a um cliente, mas contém lógica para todos os tipos de processos
dentro do aplicativo e, portanto, torna-se uma classe de Deus.
Da mesma forma, uma classe pode ter poucos métodos e campos para ser uma abstração significativa, que
Fowler chama de classe preguiçosa. Uma classe preguiçosa também pode ser criada ao longo do tempo, quando
funcionalidade é movida para outras classes, ou a classe lenta pode ter sido criada como um
stub, destinado a ser estendido, o que nunca aconteceu.

CHEIROS DE CÓDIGO DE NÍVEL DE CÓDIGO

Os cheiros de código não ocorrem apenas no nível de métodos ou classes individuais; uma base de código
como um todo também pode ter cheiros. Por exemplo, quando uma base de código contém
código em lugares diferentes, a base de código tem o cheiro de código duplicado, também chamado de código
clones. Um exemplo de código clonado é mostrado na figura 9.1. Outro exemplo de código
Machine Translated by Google

Por que o código com cheiro de código cria muita carga cognitiva 151

cheiro em uma base de código é quando ela contém vários métodos que passam continuamente cada
outra informação. Esse fenômeno é chamado de cadeia de mensagens.

Figura 9.1 Um exemplo de clones de


código; as funções foo e goo são muito
semelhantes, mas não iguais.

IMPACTO DOS CHEIROS DE CÓDIGO

A presença de um cheiro de código não implica necessariamente que o código tenha um erro. No entanto, sabe-
se que códigos com cheiros são mais propensos a conter erros. Foutse Khomh,
professor de engenharia de software na Polytechnique Montréal, no Canadá, estudou o
codebase do Eclipse, um conhecido IDE para Java e outras linguagens. Khomh inspecionado
diferentes versões da base de código do Eclipse e analisamos como os cheiros de código impactaram
erros. Ele descobriu que as classes de Deus eram um contribuinte significativo para a propensão ao erro em
todas as versões do Eclipse analisadas, e os métodos de Deus foram contribuintes significativos para
erros no Eclipse 2.1.1, 2
Khomh olhou não apenas para o impacto dos cheiros de código nos erros, mas também para as mudanças
Tendência. Ele descobriu que o código contendo cheiros também é mais provável de mudar no
futuro do que o código sem cheiro. A classe grande e os cheiros do método longo mostraram ter
um impacto significativamente negativo na propensão à mudança: classes que sofrem desses cheiros
são mais propensos a mudar do que classes sem em mais de 75% das versões do Eclipse.3

EXERCÍCIO 9.1 Pense no código que você editou ou corrigiu recentemente e que foi muito difícil de
Compreendo. Isso estava relacionado a um cheiro de código? Em que nível esses códigos
cheiros ocorrem?

9.1.2 Como o cheiro do código prejudica a cognição

Agora que cobrimos os cheiros de código em detalhes, vamos olhar para os aspectos cognitivos mais profundos.
questões ligadas a eles. Se você quiser evitar escrever código que contenha cheiros de código,
é importante entender por que eles são prejudiciais à compreensão. Vamos, portanto, explorar a conexão dos
cheiros de código aos processos cognitivos no cérebro e
especialmente à carga cognitiva.

1
Wei Le e Raed Shatnawi, “Um Estudo Empírico dos Maus Cheiros e Probabilidade de Erro de Classe na Evolução
do Sistema Orientado a Objetos Post Release, Journal of Systems and Software, vol. 80, não. 11, 2007, pp. 1120-1128,
http://dx.doi.org/10.1016/j.jss.2006.10.018.
2 Aloisio S. Cairo et al., “O impacto dos cheiros de código em bugs de software: uma revisão sistemática da literatura”, 2018,
https://www.mdpi.com/2078-2489/9/11/273.
3
Foutse Khomh et al., “Um Estudo Exploratório do Impacto dos Antipadrões na Alterabilidade do Software,” http://
www.ptidej.net/publications/documents/Research+report+Antipatterns+Changeability+April09.doc.pdf.
Machine Translated by Google

152 CAPÍTULO 9 Evitando código ruim e carga cognitiva: dois frameworks

Os cheiros de código de Fowler são baseados em trabalhos anteriores e em sua experiência pessoal na
escrita de código. Embora Fowler não faça a conexão, muitos cheiros de código podem
estar relacionado com as funções cognitivas do cérebro. Com base no que sabemos sobre o trabalho
memory e LTM, podemos interpretar o efeito do código que contém cheiros de código.
Nos capítulos anteriores, vimos diferentes formas de confusão relacionadas a diferentes processos
cognitivos. Da mesma forma, diferentes cheiros de código têm origem em diferentes
formas de processos cognitivos, que iremos esboçar.

LISTA DE PARÂMETROS LONGA, DECLARAÇÕES COMPLEXAS DE INTERRUPTOR: SOBRECARREGANDO A CAPACIDADE DO


MEMÓRIA DE TRABALHO

Sabendo o que sabemos sobre a memória de trabalho, podemos entender por que
listas de parâmetros e instruções switch complexas são difíceis de ler: ambos os cheiros são
relacionado a uma memória de trabalho sobrecarregada. Na parte 1 deste livro, explicamos que
a capacidade da memória de trabalho é tão baixa quanto 6, então faz sentido que um parâmetro
lista de mais de seis parâmetros será demais para as pessoas lembrarem.
Durante a leitura do código, será impossível manter todos os parâmetros na memória de trabalho. Como tal,
o método será mais difícil de entender.
Há, é claro, algumas nuances aqui. Nem todos os parâmetros individuais serão necessariamente tratados
como pedaços separados quando você os ler. Por exemplo, considere um
método com uma assinatura como a da próxima listagem.

Listar a assinatura do método Java 9.1 usa duas coordenadas x e duas y como parâmetros

public void line(int xOrigin, int yOrigin, int xDestination, yDestination) {}

Seu cérebro provavelmente tratará isso como dois em vez de quatro pedaços: uma origem de pedaço com um
coordenada x e y e um destino de pedaço com suas coordenadas. O limitado
número de parâmetros é, portanto, dependente do contexto e depende do conhecimento prévio
você tem sobre elementos no código. No entanto, longas listas de parâmetros são mais prováveis
sobrecarregar a memória de trabalho. O mesmo vale para instruções switch complexas.

CLASSE DE DEUS, MÉTODO LONGO: SEM POSSIBILIDADE DE CHUNKING EFICIENTE

Ao trabalhar com código, criamos abstrações continuamente. Em vez de colocar todos


funcionalidade em uma função main() , agora preferimos dividir a funcionalidade em pequenas funções
separadas com nomes significativos. Grupos de atributos e funções coerentes são combinados em classes.
Um benefício de dividir a funcionalidade em separado
funções, classes e métodos é que seus nomes servem como documentação.
Se um programador chama square(5), ele imediatamente tem uma noção do que pode ser
devolvida. Mas outro benefício dos nomes de funções e classes é que eles ajudam a
código. Por exemplo, se você vir um bloco de código que contém uma função chamada multi ples() e uma
chamada Minimum(), você pode concluir que essa função calcula
o mínimo denominador comum sem sequer inspecionar o código em detalhes. Isso é por que
cheiros de código relacionados a grandes blocos de código, incluindo classes de Deus e métodos longos,
são prejudiciais: não há características definidoras suficientes para compreender rapidamente o
código, e temos que voltar a ler o código linha por linha.
Machine Translated by Google

A influência de nomes ruins na carga cognitiva 153

CLONES DE CÓDIGO: CHUNKING DEU ERRADO

Os clones de código, ou o cheiro de duplicação, ocorrem quando uma base de código tem muito código com
pequenas diferenças.

Com o que sabemos agora sobre a memória de trabalho, podemos entender por que
clones são considerados um cheiro de código. Considere os dois métodos anteriores, mostrados novamente
na figura 9.2. Quando você vê uma chamada de função muito semelhante a foo(), como goo(), sua memória
de trabalho pode coletar informações sobre foo() do armazenamento de longo prazo. É como o seu
a memória de trabalho está lhe dizendo “Isso pode ser útil”. Em seguida, você pode inspecionar
a implementação de goo() em si. Basta olhar para ele, e fortalecido por sua
conhecimento prévio de foo(), é provável que você pense “Ah, isso é foo()”.

Figura 9.2 Duas funções com nomes


semelhantes e funcionalidades
semelhantes, mas não exatamente iguais.
Como os nomes e a implementação das
duas funções são muito semelhantes,
nossos cérebros provavelmente confundirão os dois métodos.

Assim, seu cérebro irá agrupar o método goo(), com suas pequenas diferenças de foo(),
em uma categoria com foo(), assim como algumas variantes diferentes da abertura siciliana são todas
agrupadas em “siciliano” na mente de um jogador de xadrez. Como tal, o equívoco de que goo() é um foo()
pode nascer, mesmo que retorne um valor diferente.
Vimos anteriormente neste livro que tais equívocos podem permanecer em sua mente por
muito tempo. Você pode precisar de várias exposições ao fato de que goo() não é o mesmo que
foo() para realmente perceber seu erro.

EXERCÍCIO 9.2 Reveja o código malcheiroso que você examinou no exercício 9.1. Que
processos cognitivos estiveram envolvidos na compreensão errônea do código?

9.2 A influência de nomes ruins na carga cognitiva


Neste capítulo, estamos nos concentrando na criação de código que seja fácil de entender. Até agora nós
examinaram a estrutura dos cheiros de código de Fowler, como métodos longos e códigos
clones e seu efeito na carga cognitiva.
Os cheiros de código são partes do código que sofrem de antipadrões estruturais: o código está correto,
mas não está estruturado de maneira fácil de processar. No entanto, o código também pode ter
antipadrões conceituais: o código pode ser estruturado da maneira certa, em classes organizadas
com métodos curtos, mas estes têm nomes confusos. Esses problemas com o código são
descrito no segundo quadro, antipadrões linguísticos. Porque ambos os quadros
cobrem diferentes aspectos do código, eles se complementam bem.
Machine Translated by Google

154 CAPÍTULO 9 Evitando código ruim e carga cognitiva: dois frameworks

9.2.1 Antipadrões linguísticos


Os antipadrões linguísticos foram originalmente definidos por Venera Arnaoudova, agora professora
do Estado de Washington. Arnaoudova descreve antipadrões linguísticos como inconsistências entre
elementos linguísticos no código e seus papéis. Elementos linguísticos do código
são definidos como partes de linguagem natural do código, incluindo assinaturas de métodos,
documentação, nomes de atributos, tipos ou comentários. Os antipadrões ocorrem quando os
elementos linguísticos não correspondem ao seu papel. Um exemplo simples seria uma variável
que é chamado de elemento_inicial , que não contém um elemento, mas um índice de um
elemento, ou uma variável que sugere que é um booleano, como isValid, que acaba sendo
conter um inteiro.
Antipadrões linguísticos comumente ocorrem em nomes de métodos ou funções também, quando
seu nome descreve algo que o método ou função não faz. Um exemplo disso
ocorre quando o nome de uma função parece retornar uma coleção, mas retorna

um único objeto, como um método getCustomers que retorna um booleano. Enquanto isso pode
ser um comportamento um tanto sensato em um caso em que o método está verificando se há
são clientes, também pode ser confuso.
Arnaoudova descreve seis categorias de antipadrões linguísticos, resumidas na tabela 9.2.

Tabela 9.2 seis antipadrões linguísticos de Arnaoudova

Métodos que fazem mais do que dizem

Métodos que dizem mais do que dizem

Métodos que fazem o oposto do que dizem

Identificadores cujos nomes dizem que eles contêm mais do que a entidade contém

Identificadores cujos nomes dizem que contêm menos do que a entidade contém

Identificadores cujos nomes dizem o oposto do que a entidade contém

Após definir os antipadrões linguísticos, Arnaoudova estudou sua ocorrência em


sete projetos de código aberto. Ela descobriu que esses antipadrões são relativamente comuns;
por exemplo, 11% dos setters também retornam um valor além de definir um campo. Em 2,5% de
métodos, o nome do método e o comentário correspondente deram descrições opostas do
funcionamento do método, e impressionantes 64% dos identificadores começando com
“is” acabou não sendo booleano.

VERIFIQUE OS ANTIPADRÕES LINGUÍSTICOS EM SUA BASE DE CÓDIGO Você está curioso


sobre se sua base de código sofre de antipadrões linguísticos? Baseado em
sua pesquisa, Arnaoudova criou o Linguistic Anti-Pattern Detector (LAPD),
que pode detectar antipadrões no código Java. LAPD está disponível como uma extensão
do plug-in Eclipse Checkstyle.4

4
http://www.veneraarnaoudova.ca/linguistic-anti-pattern-detector-lapd/.
Machine Translated by Google

A influência de nomes ruins na carga cognitiva 155

Embora todos possamos intuitivamente adivinhar que os antipadrões linguísticos são confusos e,
portanto, podem causar uma carga cognitiva mais alta, a ciência também confirma isso como fato.
Mas antes que possamos mergulhar nos efeitos dos antipadrões linguísticos na carga cognitiva,
devemos primeiro entender como a carga cognitiva pode ser medida.

9.2.2 Medindo a carga cognitiva


Nos capítulos anteriores, introduzimos a carga cognitiva, a sobrecarga da memória de trabalho.
Também mostramos alguns exemplos de tarefas que induzem alta carga cognitiva, por exemplo,
leitura de código quando informações relevantes estão localizadas em métodos ou arquivos diferentes,
ou leitura de código que contém muitas palavras-chave ou conceitos de programação desconhecidos.
No entanto, não abordamos como medimos a carga cognitiva.
A ESCALA PAAS PARA CARGA COGNITIVA

Ao medir a carga cognitiva, os cientistas costumam usar a Escala Paas, projetada pelo psicólogo
holandês Fred Paas, que agora é professor da Universidade Erasmus em Rotterdam, mostrado na
tabela 9.3.
A Escala de Paas tem recebido algumas críticas nos últimos anos por ser um questionário
relativamente pequeno, composto por apenas uma questão. Também não está claro se os
participantes podem distinguir de forma confiável entre, por exemplo, ter uma carga muito alta e uma
carga muito, muito alta.
Apesar de suas deficiências, a escala de Paas é comumente usada. Nos capítulos anteriores,
abordamos estratégias para leitura de código e exercícios para praticar. Ao se envolver na tarefa de
ler um código desconhecido, a Escala Paas pode ser usada para ajudá-lo a refletir sobre o código e
seu relacionamento com o código.

Tabela 9.3 Na Escala Paas, os participantes são solicitados a auto-avaliar a carga cognitiva nesta escala de 9 pontos.

Esforço mental muito, muito baixo

Esforço mental muito baixo

Baixo esforço mental

Esforço mental bastante baixo

Nem alto nem baixo esforço mental

Esforço mental bastante alto

Alto esforço mental

Esforço mental muito alto

Esforço mental muito, muito alto

EXERCÍCIO 9.3 Escolha um código desconhecido e use a Escala de Paas para avaliar o
esforço cognitivo que você teve que fazer para entender esse código. Em seguida, reflita
também sobre por que esse código causou uma certa quantidade de carga cognitiva. Este
exercício pode ajudá-lo a entender quais tipos de código são difíceis de ler.
Machine Translated by Google

156 CAPÍTULO 9 Evitando código ruim e carga cognitiva: dois frameworks

Nível de carga cognitiva Razão

Esforço mental muito, muito baixo

Esforço mental muito baixo

Baixo esforço mental

Esforço mental bastante baixo

Nem alto nem baixo esforço mental

Esforço mental bastante alto

Alto esforço mental

Esforço mental muito alto

Esforço mental muito, muito alto

MEDIÇÕES COM BASE NO OLHO

Além de métricas baseadas na percepção dos participantes, pesquisas mais recentes também
utiliza cada vez mais a biometria. Ao medir a reação do corpo a uma determinada tarefa,
é possível estimar a carga cognitiva sendo causada por qualquer que seja a pessoa
fazendo naquele momento.
Um exemplo de medição biométrica é o rastreamento ocular. Com um rastreador ocular nós
pode determinar como as pessoas estão concentradas. Isso pode ser feito, por exemplo, examinando a
taxa de piscadas de uma pessoa, que é uma medida de quantas vezes você pisca. Vários estudos
mostraram que o comportamento de piscar não é estável, mas pode diferir com base no que você está
fazendo no momento. Vários estudos também descobriram que a carga cognitiva afeta o piscar; quanto
mais difícil for uma tarefa, menos você piscará. Uma segunda métrica relacionada ao olho que pode
prever a carga cognitiva é a pupila. Estudos mostraram que tarefas mais difíceis induzem
mais carga cognitiva medida pelo tamanho da pupila.5
A hipótese atual de por que piscar se correlaciona com a carga cognitiva é que pode
indicar o quanto seu cérebro tenta maximizar sua exposição à tarefa difícil e
assim tenta obter tantos estímulos visuais quanto possível. Por razões semelhantes, alunos maiores
ocorrem ao realizar tarefas complicadas, pois com uma pupila maior, o olho pode
absorver mais informações, que é o que o cérebro procura quando está envolvido em uma tarefa difícil.

MEDIÇÕES BASEADAS NA PELE

Além das medidas com base nos olhos, a pele também pode nos dizer as
carga de uma pessoa. A temperatura da pele e a presença de suor também são indicadores de carga
cognitiva.
Embora esses métodos biométricos de medição da carga cognitiva possam parecer legais,
pesquisas mostraram que eles geralmente se correlacionam com a escala Paas, então mesmo que você
pode considerar usar seu rastreador de fitness para decidir sobre a legibilidade do seu código,
simplesmente usar o exercício 9.3 provavelmente funcionará.

5
Shamsi T. Iqbal et. al., “Task-Evolved Pupillary Response to Mental Workload in Human-Computer Interaction”, 2004, https://interruptions.net/literature/
Iqbal-CHI04-p1477-iqbal.pdf.
Machine Translated by Google

A influência de nomes ruins na carga cognitiva 157

MEDIÇÕES BASEADAS NO CÉREBRO

No capítulo 5, discutimos o scanner fMRI como um método para medir que tipo de atividades o
cérebro está realizando. As máquinas de fMRI são precisas em sua medição, mas também têm
grandes limitações. Como os participantes precisam ficar parados, eles não podem escrever
código enquanto estão na máquina. Até mesmo a leitura de código é um pouco artificial, pois só
pode ser exibida em uma tela pequena. O fato de as pessoas não poderem se mover na
máquina também limita muito suas possíveis interações com o código; por exemplo, eles não
seriam capazes de percorrer o código, clicar em identificadores para navegar até suas definições
no código ou pesquisar palavras-chave ou identificadores usando ctrl-f. Devido a essas limitações
de fMRI, medições cerebrais alternativas também são usadas.
ELETROENCEFALOGRAMA

Uma medida alternativa da atividade cerebral é o eletroencefalograma (EEG). Os dispositivos


de EEG medem a variação na atividade dos neurônios no cérebro causada pela medição da
variação na voltagem que a atividade cerebral causa. Uma segunda alternativa é o uso de
espectroscopia funcional no infravermelho próximo (fNIRS). O fNIRS também pode ser medido
com uma faixa de cabeça e, como tal, permite experimentos mais realistas do que o fMRI.
Como discutiremos na próxima subseção, o fNIRS tem sido usado para obter uma
compreensão da relação entre carga linguística e cognitiva.
Um dispositivo fNIRS usa luz infravermelha e sensores de luz correspondentes. Como a
hemoglobina no sangue absorve a luz, este dispositivo pode ser usado para detectar a
oxigenação do cérebro. A luz infravermelha se move no cérebro, mas parte dela atinge os
detectores de luz na faixa da cabeça. Ao determinar a quantidade de luz detectada pelo
detector, a quantidade de hemoglobina oxigenada e desoxigenada na área pode ser calculada.
Se o sangue estiver mais oxigenado, há um aumento da carga cognitiva. Os dispositivos fNIRS
são altamente sensíveis a artefatos de movimento e luz, portanto, os usuários devem
permanecer relativamente imóveis e não tocar no dispositivo durante a gravação. Embora isso
ainda seja muito menos intrusivo do que fMRI, é um pouco mais inconveniente do que usar uma faixa de EEG.
FNIRS FUNCIONAIS E PROGRAMAÇÃO

Em 2014, Takao Nakagawa, pesquisador do Instituto Nara de Ciência e Tecnologia no Japão,


mediu a atividade cerebral durante a compreensão do programa usando um dispositivo fNIRS
vestível.6 Os participantes do estudo de Nakagawa foram solicitados a ler duas versões de um
algoritmo escrito em C Uma versão foi uma implementação regular, e a segunda foi
deliberadamente complicada. Por exemplo, contadores de loop e outros valores foram alterados
de forma que as variáveis fossem atualizadas com frequência e irregularmente. Essas
modificações não alteraram a funcionalidade de um programa.
Enquanto usavam a faixa de cabeça fNIRS, os participantes foram apresentados às versões
original e deliberadamente complicada do programa. Para descartar a possibilidade de os
participantes aprenderem com a versão fácil e transferir esse aprendizado para a versão

6
Takao Nakagawa et al., “Quantificando a carga de trabalho mental dos programadores durante a compreensão do
programa com base na medição do fluxo sanguíneo cerebral: um experimento controlado”, https://posl.ait.kyushu-u.ac.jp/
~kamei/publications/Nakagawa_ICSENier2014. pdf.
Machine Translated by Google

158 CAPÍTULO 9 Evitando código ruim e carga cognitiva: dois frameworks

programa mais complicado, a configuração foi aleatória. Isso significava que alguns participantes
viram pela primeira vez a versão original enquanto outros viram pela primeira vez o programa modificado.
Os resultados do estudo de Nakagawa mostraram que para 8 dos 10 participantes, o
fluxo sanguíneo oxigenado enquanto estudava o complicado programa era maior do que quando
lendo os programas originais. Este resultado sugere que a carga cognitiva durante a programação pode ser
quantificada usando a medição do fluxo sanguíneo cerebral com um fNIRS
dispositivo.

9.2.3 Antipadrões linguísticos e carga cognitiva


Usando o fNIRS, os pesquisadores conseguiram medir os efeitos dos antipadrões linguísticos na carga
cognitiva. Sarah Fakhoury, atualmente estudante de pós-graduação supervisionada por
Arnaoudova, estudou as relações entre antipadrões linguísticos e
load em 2018. Quinze participantes leram trechos de código coletados de código aberto
projetos. Os pesquisadores adicionaram deliberadamente bugs aos trechos e pediram aos participantes que
encontrassem os bugs. A tarefa de detecção de bug em si não era tão importante; que
importava era que encontrar o bug tornaria os participantes capazes de compreender
o código.

Os pesquisadores fazem mais algumas adaptações ao código, de modo que tinham quatro
variantes dos trechos de código:

1 Trechos que foram alterados para conter antipadrões linguísticos

2 Snippets que foram alterados para conter inconsistências estruturais


3 Ambos os problemas

4 Sem problemas

Os participantes foram divididos em quatro grupos, e cada um dos grupos leu os bichinhos de estimação em
uma ordem diferente para descartar quaisquer efeitos de aprendizagem.
Os pesquisadores usaram um rastreador ocular para detectar onde no código as pessoas estavam lendo,
e os participantes também usaram uma máquina fNIRS para detectar a carga cognitiva. O
resultados do estudo mostraram primeiro que as partes do código onde os antipadrões linguísticos
ocorreram foram inspecionados muito mais do que o resto do código, como indicado pelo olho
resultados do rastreador.

Além disso, os resultados do dispositivo fNIRS indicam que a presença de antipadrões linguísticos no
código-fonte aumenta significativamente a média oxigenada
fluxo sanguíneo que um participante experimenta (ou seja, a carga cognitiva que é induzida por
o trecho é maior).
O que também foi interessante neste estudo em particular foi que alguns dos trechos continham
antipadrões estruturais que os pesquisadores adicionaram ao código. O código foi
formatado de forma contrária aos padrões convencionais de formatação Java; por
Por exemplo, os colchetes de abertura e fechamento não estavam em suas linhas e não eram recuados
devidamente. Eles também aumentaram a complexidade do código, por exemplo, adicionando
rotações.
Machine Translated by Google

Resumo 159

Isso permitiu que os pesquisadores comparassem o efeito da má estrutura com o efeito


de antipadrões linguísticos. Os participantes não gostaram dos trechos estruturalmente inconsistentes,
com um participante comentando que “formatação terrível aumenta severamente os
fardo." No entanto, os pesquisadores não encontraram evidências estatísticas de que inconsistências estruturais
aumentassem a carga cognitiva média que os participantes experimentaram em comparação com os trechos de
controle que não foram alterados.

9.2.4 Por que antipadrões linguísticos causam confusão


Sabemos que códigos com mais antipadrões linguísticos induzem maior carga cognitiva.
Mais estudos usando medições cerebrais são necessários para confirmar essas conexões, mas
podemos especular sobre o efeito dos antipadrões linguísticos com base no que sabemos sobre
a memória de trabalho e o LTM.
Há dois problemas cognitivos prováveis em jogo ao ler um código contendo antipadrões linguísticos. Na parte
1 deste livro, abordamos a transferência durante o aprendizado: quando você
está lendo algo desconhecido, como código que você não escreveu, seu
O LTM procura fatos e experiências relacionadas. Ao ler um nome conflitante, o
informações erradas serão apresentadas a você. Por exemplo, lendo um nome de função
retrieveElements() pode fazer você pensar em informações sobre funções retornando
uma lista de coisas. Isso lhe dá a ideia de que você pode classificar, filtrar ou dividir o retorno
elemento, o que não é verdade para um único elemento.
Uma segunda razão pela qual os antipadrões linguísticos podem confundir é que eles podem levar a “mis
chunking”, assim como clones de código. Quando você está lendo um nome de variável como
isValid, você pode simplesmente assumir que a variável é um booleano. Isso significa que não há
necessidade de seu cérebro cavar mais fundo para ver que de fato ele é usado como uma lista de possíveis retornos
valores. Ao tentar economizar energia, seu cérebro fez uma suposição errada. Como nós vimos
anteriormente, tais suposições podem perdurar por longos períodos de tempo.

Resumo
ÿ Odores de código, como métodos longos, indicam problemas estruturais com o código. Lá
são diferentes razões cognitivas pelas quais os cheiros de código causam uma carga cognitiva mais alta.
O código duplicado, por exemplo, torna mais difícil o código em pedaços corretamente, enquanto
longas listas de parâmetros são pesadas em sua memória de trabalho.

ÿ Existem diferentes maneiras de medir a carga cognitiva, incluindo sensores biométricos


como medir a taxa de piscar ou a temperatura da pele. Se você quiser medir seu
própria carga cognitiva, a Escala de Paas normalmente é um instrumento confiável. ÿ
Antipadrões linguísticos indicam lugares em uma base de código onde o código faz algo diferente do que os
nomes envolvidos sugerem, levando a uma carga cognitiva mais alta.
Isso provavelmente é causado pelo fato de seu LTM encontrar fatos errados ao tentar
apoiar o seu pensamento. Antipadrões linguísticos também podem levar a fragmentação errada
porque seu cérebro assume um significado de código que não é realmente implementado.
Machine Translated by Google

Ficando melhor em
resolvendo problemas complexos

Este capítulo abrange


ÿ Comparar o papel que diferentes sistemas de memória
desempenham na resolução de problemas

ÿ Investigar como a automatização de pequenas habilidades pode


ajudá-lo a resolver problemas maiores e mais difíceis

ÿ Entender como fortalecer seu LTM para resolver problemas


com maior facilidade

Nos últimos capítulos, analisamos principalmente o que você não deve fazer ao codificar
e porque. Investigamos o efeito de nomes ruins no capítulo 8 e o impacto de
código cheira a sua capacidade de entender código no capítulo 9.
No início do livro, no capítulo 6, discutimos diferentes estratégias para apoiar
sua memória de trabalho ao resolver problemas de programação. Este capítulo novamente
abrange técnicas para ajudá-lo na resolução de problemas, mas concentra-se no fortalecimento
seu LTM.

160
Machine Translated by Google

O que é resolução de problemas? 161

Vamos primeiro investigar o que significa resolver um problema. Depois de explorar o problema
resolvendo em profundidade, mergulhamos em como melhorar nisso. Ao final deste capítulo, você
conhecerá duas técnicas para melhorar suas habilidades de programação e resolução de problemas.
A primeira técnica que abordaremos é a automatização (ou seja, ser capaz de realizar pequenas tarefas
sem pensar neles). Isso é útil porque quanto menos tempo você gasta para descobrir pequenas coisas, mais fácil
é resolver problemas difíceis. Em seguida, exploraremos a resolução de problemas a partir do código escrito de
outras pessoas como um meio de melhorar suas próprias habilidades de resolução de problemas.

10.1 O que é resolução de problemas?


O objetivo deste capítulo é ensinar estratégias que o ajudarão a melhorar sua
habilidades de resolução de problemas examinando o papel do LTM na resolução de problemas. Mas antes de nós
pode lhe dizer como resolver melhor os problemas, primeiro vamos mergulhar no que significa
envolver-se na resolução de problemas.

10.1.1 Elementos de resolução de problemas

A resolução de problemas tem três elementos importantes: ÿ

O estado da meta, que é o que queremos alcançar. Quando atingimos o estado objetivo,
o problema é considerado resolvido.
ÿ O estado inicial de onde o problema deve ser resolvido ÿ As
regras que ditam como podemos alcançar o estado objetivo a partir do estado inicial
Por exemplo, considere jogar jogo da velha. Nessa situação, o estado inicial é um vazio
campo, seu estado desejado é três cruzes seguidas, e as regras são que você pode colocar um
cruzar em qualquer campo vazio no tabuleiro. Quando você adiciona uma caixa de pesquisa a um site existente,
seu estado inicial é a base de código existente e seu estado desejado pode ser
passando em testes de unidade ou um usuário satisfeito. As regras do problema na programação muitas vezes
vêm na forma de restrições, como implementar o recurso em JavaScript ou não
quebrando testes adicionais ao implementar este novo recurso.

10.1.2 Espaço de estado

Todos os passos que poderíamos considerar ao resolver um programa são chamados de estado do problema
espaço. Ao jogar jogo da velha, todos os campos possíveis são o espaço de estados. Para pequenos problemas
como jogo da velha, todo o espaço de estados pode ser visualizado. A Figura 10.1 mostra um pequeno
parte do espaço de estados de um jogo da velha.
Por exemplo, ao adicionar um botão a um site, todos os programas JavaScript possíveis
são o espaço de estados. Cabe ao solucionador de problemas fazer os movimentos certos ou adicionar o
linhas de código certas para atingir a meta inicial. Em outras palavras, a resolução de problemas significa
percorrendo o espaço de estados da maneira ideal, alcançando o estado objetivo em poucos passos
possível.
Machine Translated by Google

162 CAPÍTULO 10 Melhorando na resolução de problemas complexos

Linha de x: 0

Linha de x: 1

Figura 10.1 Uma parte do espaço de


estados de um jogo da velha, onde as
setas de link indicam movimentos para os e setas azuis

Linha de x: 2 são movimentos para x's. O estado


objetivo para x é obter três x's seguidos.

EXERCÍCIO 10.1 Examine algo que você criou com código nos últimos
dias. Quais eram os aspectos desse problema?

ÿ Qual era o estado de meta que você queria alcançar?


ÿ Como o estado da meta foi verificado: manualmente por você, por outra pessoa ou com unidade
testes ou testes de aceitação?
ÿ Qual foi o estado inicial?

ÿ Quais regras e restrições aplicadas?

10.2 Qual é o papel do LTM quando você resolve problemas de programação?


Agora que definimos o que significa resolver um problema, podemos mergulhar no que
acontece em seu cérebro quando você faz. O Capítulo 6 abordou o que acontece no seu trabalho
memória quando você resolve problemas de programação. Se você experimentar muita carga cognitiva, seu
cérebro não pode processar adequadamente e a programação fica mais difícil. Contudo,
o LTM também desempenha um papel na resolução de problemas, como exploraremos neste capítulo.

10.2.1 A resolução de problemas é um processo cognitivo por si só?


Algumas pessoas pensam que a resolução de problemas é uma habilidade genérica e, portanto, também um
processo específico em seu cérebro. O matemático húngaro George Pólya é um famoso pensador da
Solução de problemas. Em 1945, Pólya escreveu um livro curto e famoso chamado How to Solve It.
Seu livro propõe um “sistema de pensamento” para resolver qualquer problema envolvendo três etapas:

1 Entendendo o problema
2 Elaborando um
plano 3 Realizando o plano
No entanto, apesar da popularidade das abordagens genéricas, a pesquisa tem consistentemente
mostraram que a resolução de problemas não é uma habilidade genérica nem um processo cognitivo. Lá
são duas razões pelas quais os métodos genéricos de resolução de problemas não funcionam tão bem, e ambos
têm a ver com o papel do LTM.
Machine Translated by Google

Qual é o papel do LTM quando você resolve problemas de programação? 163

VOCÊ USA LTM AO RESOLVER PROBLEMAS

Primeiro, quando você resolve um problema, leva em consideração o conhecimento sobre o estado
do objetivo desejado e as regras pelas quais temos que jogar. O problema em si influenciará as
soluções que podemos projetar. Vamos considerar a influência do conhecimento prévio nas soluções
com um exemplo de programação. Suponha que você tenha que implementar um código para
detectar se uma determinada string de entrada s é um palíndromo. Pedimos que você escreva o
código para este problema em Java, APL e BASIC. Vamos investigar os passos de Polya e ver como
eles se sairiam durante a programação.

ÿ Compreender o problema.
Assumimos que para você, sendo um programador, entender o problema não é um
problema. Você provavelmente será capaz de formular alguns casos de teste sólidos para
verificar seu código também. ÿ Elabore um plano (traduza).

A segunda etapa do método de Polya é mais difícil de fazer. Seu plano depende muito
dos recursos da linguagem de programação na qual você implementará a solução.

A linguagem em questão, por exemplo, tem um método ou função inversa? Nesse caso,
você pode simplesmente verificar se a string s é igual a reverse(s). Você deve saber que em
Java você pode usar StringBuilder, que tem uma função reverse() , mas esse método está
disponível para BASIC e APL?
Não ter acesso às informações, aos blocos de construção da solução, dificulta a criação de
um plano.
ÿ Executar o plano (resolver).
A terceira etapa do método de Polya também é influenciada por sua compreensão da
linguagem de programação em questão. Posso dizer que o APL realmente tem uma função
reverse() (o BASIC não tem), mas você viu um pouco do APL agora e sabe que não será
chamado reverse(), porque todas as palavras-chave do APL são operadores, portanto, a
execução do plano ainda é um desafio.

É MAIS FÁCIL PARA O SEU CÉREBRO RESOLVER PROBLEMAS FAMILIARES

Há uma segunda razão pela qual os métodos genéricos de solução de problemas geralmente ficam
aquém que também se relaciona com o funcionamento do LTM. No capítulo 3, explicamos que as
memórias no LTM são armazenadas como uma rede em relação umas às outras. Anteriormente
neste capítulo, também abordamos o fato de que, enquanto pensa em um problema, o cérebro
recupera informações do LTM, que podem ser relevantes para o problema em questão.
O uso de uma técnica genérica de solução de problemas como a de Polya “desenhe um plano”
cria um problema cognitivo. Você pode ter muitas estratégias úteis armazenadas em seu LTM, que o
cérebro tenta recuperar ao resolver o problema. Quando tentamos resolver o problema de forma
genérica, no entanto, as estratégias relevantes podem não ser encontradas. Conforme descrevemos
no capítulo 3, o LTM precisa de pistas para recuperar as memórias certas. Quanto mais específica a
pista, maior a probabilidade de encontrarmos a memória certa. Por exemplo, quando você tem que
realizar a divisão da cauda, é improvável que pensar em um plano dê ao seu LTM pistas suficientes
Machine Translated by Google

164 CAPÍTULO 10 Melhorando na resolução de problemas complexos

para encontrar a abordagem armazenada na memória. Pensar em coisas como divisão de cauda ou subtração de
múltiplos do divisor tem maior probabilidade de resultar no plano certo.
Como abordamos no capítulo 7, transferência de conhecimento de um domínio, como xadrez,
para outro domínio, como a matemática, é improvável de acontecer. Da mesma forma, a transferência de
o domínio muito genérico da solução de problemas de volta para outros domínios não é muito provável.

10.2.2 Como ensinar seu LTM a resolver problemas

Vimos que a resolução de problemas não é um processo cognitivo. Isso nos traz a isso
pergunta: Em vez disso, como devemos treinar para a resolução de problemas? Para explorar isso em mais
profundidade, precisamos mergulhar ainda mais fundo em como o cérebro pensa. No início do livro, nós
explicou que os pensamentos são formados na memória de trabalho. Vimos também que seu
a memória de trabalho não forma pensamentos sozinha, mas opera em forte colaboração
que tanto o LTM quanto o STM.

Quando você pensa em um determinado problema, digamos que implemente um botão de classificação em
uma aplicação web, sua memória de trabalho tomará as decisões sobre o que você implementa. No entanto, antes
que sua memória de trabalho possa tomar tal decisão, ela precisa
fazer duas coisas. A primeira é obter informações do STM sobre o contexto do problema, como os requisitos para
o botão ou o código existente que você acabou de ler.
Ao mesmo tempo, o LTM é pesquisado por conhecimento prévio relevante. Memórias relevantes que você
pode ter, como como implementar classificação ou informações
sobre a base de código, também são enviados para a memória de trabalho. Para entender o problema
resolvendo melhor, precisamos explorar esse segundo processo de busca do LTM.

10.2.3 Dois tipos de memórias que desempenham um papel na resolução de problemas

Mais adiante neste capítulo, exploraremos duas técnicas para fortalecer sua capacidade de resolução de problemas.
Habilidades. Antes disso, no entanto, precisamos explorar diferentes tipos de memórias que as pessoas têm.
e qual o papel que desempenham na resolução de problemas. Compreender as diferentes formas de
memórias são importantes porque tipos diferentes são criados de maneiras diferentes,
O LTM pode armazenar diferentes tipos de memória, que estão descritos na figura 10.2.
Primeiro, há a memória procedural (às vezes chamada de implícita) , que é a memória para
habilidades motoras ou habilidades que você não está conscientemente ciente. As memórias implícitas são, por
por exemplo, saber amarrar cadarços ou andar de bicicleta.
O segundo tipo de memória que desempenha um papel na resolução de um problema é a declarativa
(às vezes chamado de memória explícita) . Há fatos que você pode lembrar, e você também
sei que você os conhece, como o fato de Barack Obama ter sido o 44º presidente da
nos Estados Unidos, ou que a maneira de escrever um loop for em Java é para (i = 0; i < n; i++).
Conforme mostrado na figura 10.2, as memórias declarativas podem ser subdivididas em duas categorias:
memória episódica e memória semântica. Memórias episódicas são o que muitas vezes
queremos dizer quando usamos coloquialmente a palavra “memória”. Essas são memórias de experiências, como
ir ao acampamento de verão quando você tinha 14 anos, conhecer seu cônjuge pela primeira vez.
tempo, ou passar três horas perseguindo um bug apenas para descobrir que havia um erro em um
teste de unidade.
Machine Translated by Google

Qual é o papel do LTM quando você resolve problemas de programação? 165

A outra parte da memória declarativa é chamada de memória semântica . Memória semântica


é memória para significados, conceitos ou fatos, como aquele sapo em francês é “grenouille”,
ou 5 vezes 7 é igual a 35, ou uma classe em Java é usada para combinar dados e funcionalidade.
As memórias semânticas são as memórias que treinamos no capítulo 3 com flashcards. Memórias episódicas
são criadas sem você gastar esforço adicional, embora, similar
para memórias semânticas, a força de recuperação será maior para memórias que você tem
pensado muitas vezes.

QUE TIPOS DE MEMÓRIAS TÊM UM PAPEL QUANDO VOCÊ RESOLVE PROBLEMAS?


Todas essas formas de memória desempenham um papel na programação, conforme descrito na figura 10.2
Ao considerar as memórias que você usa para programação, a memória explícita pode ser
o primeiro que me vem à mente. Durante a programação, um programador deve se lembrar
como construir um loop em Java. No entanto, outras formas de memória também desempenham um papel, como
ilustrado pela figura 10.2

Recordações

Declarativo
Processual
ou
ou Figura 10.2 Existem diferentes
explícito tipos de memórias. A memória
implícito
procedural (ou implícita) mostra
como fazer algo. A memória
declarativa (explícita) consiste em
Semântica memórias das quais estamos
Episódico explicitamente cientes. A memória
declarativa é dividida em coisas que
você experimentou e que são
a2 + b2 = c2 armazenadas na memória episódica
e fatos que você conhece que são armazenados na me

A memória episódica é usada quando você se lembra de como resolveu um problema no passado.
Quando você tem que resolver um problema envolvendo hierarquia, por exemplo, você pode
lembre-se que você usou uma árvore no passado. A pesquisa mostra que os especialistas confiam especialmente
fortemente na memória episódica ao resolver problemas. De certa forma, os especialistas recriam,
em vez de resolver problemas familiares. Isso significa que, em vez de encontrar uma nova solução, eles confiam
em soluções que funcionaram anteriormente para problemas semelhantes. No capítulo 10, vamos mergulhar
mais fundo em como podemos fortalecer memórias episódicas para nos tornarmos
melhores solucionadores de problemas.

Além de ambas as formas de memória explícita, as atividades de programação também


memória implícita, conforme ilustrado na figura 10.3. Por exemplo, muitos programadores podem
tipo de toque, que é a memória procedural. Além do alfabeto, existem várias teclas que você pode usar sem
atenção explícita, como pressionar ctrl-z quando você
cometer um erro ou adicionar automaticamente um colchete de fechamento a um colchete de abertura. Dentro
atividades de resolução de problemas, a memória implícita também pode desempenhar um papel, por exemplo, quando você
Machine Translated by Google

166 CAPÍTULO 10 Melhorando na resolução de problemas complexos

colocar automaticamente um ponto de interrupção em uma linha onde você suspeita que um bug possa estar presente.
Às vezes, o que chamamos de intuição, de fato, acontece quando você resolve um problema semelhante ao
um que você já resolveu antes; você só sabe o que fazer sem realmente saber como fazer
isto.

Recordações

Processual Declarativo
ctrl-c
ou ou
ctrl-v
implícito explícito

Episódico Semântica

Figura 10.3 Diferentes tipos de


for (i=o; i<n; i++){
memórias e como elas
}
desempenham um papel na programação

DESAPRENDIZADO

Embora tenhamos visto que a memória implícita pode ajudá-lo a executar tarefas conhecidas rapidamente,
ter memórias implícitas também pode ser prejudicial. No capítulo 7, discutimos a ideia de
transferência negativa: saber algo prejudica aprender outra coisa. Tendo muito
a memória implícita também pode prejudicar sua flexibilidade. Por exemplo, uma vez que você aprendeu
como tocar no teclado Qwerty, aprender a usar um teclado Dvorak será
mais difícil do que se você nunca tivesse aprendido Qwerty. Isso se deve em parte ao fato de você
têm um grande conjunto de memórias implícitas de como fazer as coisas.
Você pode ter experimentado a dificuldade de desaprender a memória implícita, também,
se você já aprendeu uma segunda linguagem de programação com uma sintaxe bem diferente
da primeira língua que você aprendeu. Por exemplo, ao passar de C# ou Java para
Python, é provável que você inconscientemente digite chaves em torno de blocos ou funções por um tempo. Eu
pessoalmente mudei de C# para Python anos atrás, e ainda
digite foreach em vez de for ao iterar sobre uma lista, que é devido à memória implícita
Eu construí ao programar C# e isso ainda está fortemente presente.

EXERCÍCIO 10.2 Na próxima vez que você escrever código, tente monitorar ativamente o
memórias que você usa.

Use a tabela a seguir para refletir sobre que tipo de programas ou problemas ativam qual tipo de memória.
Pode ser interessante fazer este exercício algumas
vezes para programas diferentes. Se você fizer o exercício algumas vezes e seguir
seu progresso por um tempo, é provável que você veja que, para linguagens ou projetos de programação
menos familiares, você confia mais na memória semântica,
enquanto para situações mais familiares, você usa mais procedimentos e episódios
recordações.
Machine Translated by Google

Automatização: criando memórias implícitas 167

Programa ou problema Processual Episódico Semântica

10.3 Automatização: Criando memórias implícitas


Agora que entendemos por que a resolução de problemas é difícil e como os diferentes tipos de
memórias desempenham um papel na resolução de problemas, vamos explorar como você pode melhorar seu
habilidades de resolução de problemas de duas maneiras. A primeira técnica é a automatização. Uma vez que você tenha
praticou uma habilidade tantas vezes que você pode fazê-la sem pensar nela, como andar, ler ou amarrar os
cadarços, dizemos que você automatizou essa habilidade.
Muitas pessoas automatizaram as habilidades do dia-a-dia, como dirigir ou andar de bicicleta, mas também
habilidades específicas de domínio, como matemática. Por exemplo, você pode ter aprendido a fatorar
equações como x2 + y2 + 2xy. Se você é uma das pessoas que automatizou a lei distributiva, você pode
traduzir imediatamente esta fórmula para (x + y)2 , esforço do que ler esta linha. O importantesem
aquimais
é que ser
capaz de fatorar
equações sem esforço permite realizar cálculos mais complexos. eu frequentemente
pense na automatização como um processo que desbloqueia novas habilidades em um jogo. Uma vez que você pode
salto duplo em um jogo, permite que você alcance áreas de um nível que você não poderia alcançar
antes de.

Por exemplo, uma vez que você pode fatorar equações com grande facilidade, você pode olhar para um
equação como a que segue e ver imediatamente que a resposta é (x + y). Se vocês
não automatizou a fatoração, este problema será muito mais difícil de fazer, se não
impossível.

x2 y2+ 2xy
+
----------------------------------

()x+

Assim, a automatização das habilidades de programação é a chave para poder resolver problemas maiores e mais
problemas complexos. Mas como você chega ao ponto de ter habilidades automatizadas?
Para saber como automatizar habilidades, primeiro precisamos explorar como fortalecer seu
memórias de programação implícitas. No início do capítulo, discutimos o fato de que
às vezes, memórias implícitas podem atrapalhar quando se muda para uma nova programação
linguagem, por exemplo, quando você continua inserindo chaves em um problema Python.
Embora você possa pensar que é um pequeno erro, isso causa alguma carga cognitiva. Como
abordada no capítulo 9, a carga cognitiva indica o quanto seu cérebro está ocupado ou cheio. Quando você
experimentar muita carga cognitiva, o pensamento pode se tornar muito difícil. A coisa interessante sobre
memórias implícitas é que quando você treinou memórias implícitas
bem o suficiente, seu cérebro quase não precisa de energia para usá-los. Por exemplo, quando
você sabe andar de bicicleta, ou como tocar o tipo, você pode fazê-lo sem nenhum esforço. Porque
Machine Translated by Google

168 CAPÍTULO 10 Melhorando na resolução de problemas complexos

essas tarefas não criam quase nenhuma carga cognitiva, você pode andar de bicicleta e também comer um sorvete ou
dirigir um carro enquanto fala.

10.3.1 Memórias implícitas ao longo do tempo


Quanto mais memórias implícitas você tiver para programação, mais fácil será resolver
problemas maiores porque você terá mais carga cognitiva de sobra. Como você cria memórias mais implícitas?
Para entender isso, temos que mergulhar em como eles são
criado no cérebro.

No capítulo 4, abordamos como criar memórias, por exemplo, usando um baralho de


flashcards nos quais você escreve fatos que deseja lembrar e revisitando esses cartões
frequentemente. No entanto, esses tipos de técnicas são úteis principalmente para o conhecimento declarativo.
Fatos armazenados em sua memória explícita precisam de sua atenção explícita para serem armazenados. Por
Por exemplo, você provavelmente levou algum tempo para memorizar que um loop for em Java está escrito
for (i = 0; i < n; i++){}, e você sabia que realmente queria aprender. Isto é
por que também a chamamos de memória explícita; ele precisa de sua atenção explícita para ser armazenado.
As memórias implícitas, por outro lado, são criadas de uma forma diferente: pela repetição.
Quando você era criança, você tentou comer sopa com uma colher muitas vezes, e depois de um tempo
você sabia como fazer. A memória de como fazê-lo foi criada através da prática e não
do que pensar. É por isso que chamamos isso de memória implícita. Memórias implícitas são formadas
em três fases diferentes, conforme ilustrado na figura 10.4.

FASE COGNITIVA
Primeiro, alguém que está aprendendo algo novo está na fase cognitiva. Nesta fase,
uma nova informação precisa ser dividida em partes menores e você tem que pensar explicitamente sobre a
tarefa em mãos.
Por exemplo, quando você aprendeu a indexar uma lista baseada em zero, provavelmente
precisava gastar energia para acompanhar esse índice, conforme mostrado na parte mais à esquerda da figura
10.4. Na fase cognitiva, os esquemas são formados ou atualizados. Por exemplo, quando
você aprendeu a indexar uma lista começando do zero, você já tinha um esquema salvo no seu cérebro
para contar fora da programação. Esse esquema ditava que a contagem começa em um
e precisava ser adaptado para incluir também a possibilidade de começar do zero.

Fase Fase Fase


cognitiva associativa autônoma
[7, 4, 2, 1, 5]
Comece em 0, então
elemento 3 é Apenas sempre
subtrair 1 lista[4]
o quarto

Figura 10.4 As três fases de armazenamento de informações: fase cognitiva, fase associativa
e fase autônoma
Machine Translated by Google

Automatização: criando memórias implícitas 169

FASE ASSOCIATIVA
A fase associativa é a próxima. Nesta fase, você precisa repetir ativamente as novas informações até que surjam
padrões de resposta. Você vê um colchete de abertura e fica nervoso se não vê o de fechamento. Você também
pode perceber que digitar os dois colchetes
é uma ótima estratégia para não esquecer o fechamento. Em outras palavras, ações efetivas são
ações lembradas e ineficazes são descartadas.

Quanto mais difícil é uma tarefa, mais tempo leva para completar a fase associativa. Mais fácil
fatos ou tarefas são lembrados mais rapidamente. No exemplo da contagem em zero, após
um tempo você pode perceber que você pode simplesmente pensar no elemento que deseja recuperar
e subtraia 1 dele para chegar ao índice correto.

FASE AUTÔNOMA
Finalmente, chega-se à fase autônoma (também chamada de fase processual), onde
a habilidade é aperfeiçoada. Por exemplo, você atingiu a fase autônoma quando
index em uma lista e você sempre faz isso corretamente, não importa o contexto, o tipo de dados ou
as operações da lista. Quando você vê uma lista e uma operação de lista, agora você pode dizer a sua
número imediatamente, sem depender de contar ou pensar explicitamente.
Quando você atinge a fase autônoma, dizemos que você automatizou a habilidade.
Você pode executar a tarefa sem nenhum esforço, e realizar a habilidade não irá adicionar
a carga cognitiva que um problema representa.
Para experimentar o poder da automatização, veja o exercício 10.3. Se você é um programador Java
experiente, provavelmente pode preencher os espaços em branco no código sem
a pensar nisso. O padrão de um loop for é tão conhecido que você pode completá-lo sem pensar nos limites,
mesmo em uma situação um pouco mais incomum, como um
laço reverso.

EXERCÍCIO 10.3 Conclua esses programas Java completando as partes que faltam
sobre __ o mais rápido possível:

for (int i = ; <= 10; i = i + 1)


__{
System.out.println(i);
}

public class FizzBuzz {


public static void main(String[] args) {
for (int numero = 1; numero <= 100; __++) {
if (número % 15 == 0) {
System.out.println("FizzBuzz");
} else if (número % 3 == 0) {
System.out.println("Fizz");
} else if (número % 5 == 0) {
System.out.println("Buzz");
} senão {
System.out.println(number);
}
}
}
}
Machine Translated by Google

170 CAPÍTULO 10 Melhorando na resolução de problemas complexos

public printReverseWords(String[] argumentos) {


for (String linha: linhas) {
String[] palavras = line.split("\\s");
for (int i = palavras.comprimento - 1; i >= 0; i__)
System.out.printf("%s ", palavras[i]);
System.out.println();
}
}

10.3.2 Por que a automatização vai fazer você programar mais rápido
Ao criar um grande repositório de técnicas (os céticos podem chamá-las de truques), podemos
criar uma caixa de ferramentas cada vez maior de novas técnicas. Gordon Logan, psicólogo americano, argumenta
que a automatização é feita recuperando memórias da parte episódica do LTM, na qual também são armazenadas
memórias regulares sobre a vida cotidiana.
A execução de uma tarefa como fatorar uma equação ou ler uma carta cria uma nova memória, que é uma
instância de uma memória sobre a tarefa. Porque cada memória é vista como
uma instância de uma classe abstrata, como a classe “memórias sobre fatoração”, a teoria é
chamada de teoria da instância.
Quando você é confrontado com uma tarefa semelhante, em vez de raciocinar sobre o
tarefa — como alguém que não tem memórias de instância suficientes pode fazer — você pode lembrar como fez
antes e aplicar o mesmo método. De acordo com Logan, a automatização está completa quando você confia
totalmente na memória episódica e não usa nenhum
raciocínio em tudo. Este desempenho automático de tarefas é rápido e sem esforço porque
recuperação da memória é mais rápida do que pensar ativamente na tarefa em mãos e pode ser
feito com pouca ou nenhuma atenção consciente. Se você automatizou totalmente uma tarefa, você
também não sente necessidade de voltar e verificar seu trabalho, o que você pode ficar tentado a fazer
ao completar uma tarefa pelo raciocínio.
Para muitas das habilidades que você precisa para programação e resolução de problemas, você tem
provavelmente atingiu a fase autônoma até agora. Escrevendo um loop for, indexando em uma lista,
ou criar uma classe são habilidades que você provavelmente automatizou. Assim, essas tarefas
não adicionar à sua carga cognitiva durante a programação.
Dependendo de sua experiência e habilidades, no entanto, há tarefas que você provavelmente
ainda lutando. Como mencionei antes, eu pessoalmente lutei com o loop for
quando comecei a aprender Python. Não que eu não conseguisse lembrar a sintaxe do for; eu
tinha até usado flashcards para praticar a sintaxe! No entanto, muitas vezes quando eu tentei digitar
as palavras, pois cada uma saiu de meus dedos e não para. Minhas memórias implícitas
precisava ser religado. Antes de mergulharmos em técnicas para religar suas memórias,
seria uma ótima idéia diagnosticar suas próprias habilidades para que você saiba onde melhorar.

EXERCÍCIO 10.4 Inicie uma nova sessão de programação enquanto pensa no


tarefas ou habilidades que você está usando durante a programação. Para cada habilidade, examine em
em que nível você automatizou a habilidade ou tarefa e escreva os resultados no
tabela a seguir. Estas perguntas podem ajudá-lo a decidir o nível de suas habilidades:
Machine Translated by Google

Automatização: criando memórias implícitas 171

ÿ Você precisa dedicar atenção explícita à tarefa em mãos isoladamente? que


significa que você ainda está na fase cognitiva.
ÿ Você consegue fazer a tarefa, mas está contando com truques? Você provavelmente está no associativo
Estágio.
ÿ Você consegue realizar a tarefa com facilidade enquanto pensa em outros problemas?
Você atingiu a fase autônoma.

Tarefa ou habilidade Cognitivo Associativo Autônomo

10.3.3 Melhorando as memórias implícitas


Agora que entendemos os três níveis diferentes em que você pode dominar uma habilidade, vamos
veja como você pode usar a prática deliberada para melhorar as habilidades que ainda não atingiram
a fase autônoma. Como abordamos no capítulo 2, a ideia de prática deliberada é
use tarefas muito pequenas e execute-as repetidamente até atingir a perfeição.
Por exemplo, no treinamento intervalado esportivo, uma maneira deliberada de aumentar sua velocidade é
correr, e na música, as escadas de tom são uma prática deliberada para treinar o posicionamento dos dedos.
Na programação, a prática deliberada não é comumente usada. Quando você está lutando para criar loops
for sem erros, digitar deliberadamente 100 loops for não é
algo comumente feito na cultura de programação. No entanto, construir esses pequenos
habilidades irão ajudá-lo a resolver problemas maiores com maior facilidade porque libera habilidades cognitivas
carga para esses problemas maiores.
A execução da prática deliberada pode ser feita de diferentes maneiras. Primeiro, você pode escrever um
muitos programas semelhantes, mas diferentes, para os quais você precisa da habilidade que deseja praticar.
Se você estiver praticando loops for, por exemplo, escreva muitas formas diferentes de loops for:
para frente, para trás, usando uma variável de passo com diferentes etapas e assim por diante.
Quando você está lutando com um conceito de programação mais complexo, você também pode
considere adaptar programas em vez de escrevê-los do zero. Adaptação
programas ajuda você a se concentrar em como os novos conceitos diferem daqueles que você já conhece. Por
Por exemplo, se você está lutando com a compreensão da lista, você pode primeiro escrever muitos
programas diferentes que usam loops. Em seguida, você pega os programas e
adaptar o código até que eles usem compreensões de lista. Também pode funcionar bem para reverter o
alterações no código manualmente para refletir sobre a diferença de uma perspectiva diferente.
Ao comparar ativamente as diferentes formas de código, a equivalência da programação
conceitos é fortalecido em sua memória, como vimos no capítulo 3 com o uso de
flashcards.
Machine Translated by Google

172 CAPÍTULO 10 Melhorando na resolução de problemas complexos

Assim como com os flashcards, a repetição espaçada é a chave para o aprendizado. Reserve algum tempo
todos os dias para praticar e continuar até que você possa executar as tarefas de forma consistente sem qualquer
esforço. Eu quero enfatizar novamente como essa técnica é incomum na programação, então pode parecer
estranho, mas continue tentando. É realmente como levantamento de peso; cada
a repetição torna você um pouco mais forte.

10.4 Aprendendo com o código e sua explicação


Neste capítulo, vimos que não existe solução genérica de problemas.
habilidades e que simplesmente fazer muita programação dificilmente fará de você um melhor solucionador de
problemas. Também vimos que você pode usar a prática deliberada para melhorar pequenos
habilidades de programação. Embora seja necessário dominar pequenas habilidades no nível autônomo,
não é suficiente para resolver problemas maiores.
Uma segunda técnica que você pode usar para melhorar suas habilidades de resolução de problemas é
estudar deliberadamente como os outros resolveram problemas. Soluções de como outras pessoas têm
problemas resolvidos são frequentemente chamados de exemplos trabalhados.

O professor australiano John Sweller, que também introduziu a ideia de carga cognitiva,
coberto no capítulo 4, pesquisou extensivamente a importância de
estratégias para habilidades de resolução de problemas.
Sweller ensinou matemática às crianças fazendo com que elas resolvessem equações de álgebra, mas
logo ele ficou frustrado com o quão pouco eles estavam aprendendo trabalhando apenas em problemas tradicionais
de álgebra. Nesse ponto, Sweller se interessou em experimentar
a forma como a resolução de problemas é ensinada. Para obter mais informações, Sweller realizou uma série de
experimentos na década de 1980. Nesses experimentos, Sweller estudou 20 alunos do nono ano
(crianças de 14 a 15 anos) em uma escola secundária australiana. Ele dividiu os alunos em dois
grupos, que foram solicitados a resolver equações típicas de álgebra, como “a = 7 – 4a;
resolver para um.”

No entanto, houve diferença entre os grupos, conforme ilustrado na figura 10.5.


Ambos os grupos resolveram as mesmas equações de álgebra, mas o grupo 2 simplesmente resolveu as equações

a = b – ca
a + ca = b
a = 7 - 4a
a (1 + c) = b
O que é uma?
a=b
1+c

Figura 10.5 Ambos os grupos de crianças


resolveram os mesmos problemas de álgebra,
mas o grupo da esquerda recebeu receitas,
chamadas de exemplos trabalhados, que
ensinam aos alunos como resolver o problema passo a passo.
Machine Translated by Google

Aprendendo com o código e sua explicação 173

sem mais ajuda, assim como você provavelmente também resolveu equações de álgebra em alta
escola. O Grupo 1 recebeu as equações de álgebra, mas também recebeu exemplos trabalhados das
equações, que explicam como resolver os problemas em detalhes. Você pode pensar em trabalhar
exemplos como receitas que descrevem em detalhes os passos necessários para resolver as equações.
Depois que os dois grupos de crianças terminaram as equações, Sweller e Cooper compararam o
desempenho dos alunos. Provavelmente não é uma surpresa que o
o primeiro grupo se saiu melhor; afinal, eles têm receitas para resolver os problemas. O grupo 1 fez
espetacularmente melhor; eles resolveram as equações cinco vezes mais rápido que o grupo 2.
Sweller e Cooper também testaram o desempenho de ambos os grupos em diferentes problemas porque
é isso que faz com que algumas pessoas relutem em ensinar com essas receitas; a
ideia de que as crianças só poderão seguir cegamente as receitas e não
nada. E aqui está o kicker: as crianças do grupo 1 também tiveram melhor desempenho em diferentes
problemas, para os quais poderiam ser usadas regras de cálculo (que estavam presentes no
receita), como subtrair o mesmo valor de ambos os lados de uma equação ou dividir
ambos os lados da equação pelo mesmo.
O efeito do exemplo trabalhado foi replicado em muitos estudos para diferentes idades
grupos e assuntos, incluindo matemática, música, xadrez, esportes e programação.

10.4.1 Um novo tipo de carga cognitiva: carga alemã


Para muitos programadores profissionais, os resultados de Sweller podem ter sido surpreendentes. Nós
muitas vezes assumimos que, se queremos que as crianças sejam boas solucionadoras de problemas, devemos deixá-las
resolver problemas; se queremos ser bons programadores, devemos programar muito. No entanto, os
resultados de Sweller parecem mostrar que não é o caso, e agora vamos mergulhar no
detalhes de por que isso é verdade. Por que o grupo que recebeu as receitas se saiu melhor do que
o grupo que teve que resolver os problemas sozinho? A explicação Sweller
oferece diz respeito à carga cognitiva da memória de trabalho.
Aprendemos estas coisas: a memória de trabalho é o STM aplicado a um determinado problema. Assim,
enquanto o STM pode conter apenas dois a seis itens, conforme explicado no capítulo 2, o
a memória de trabalho só pode processar cerca de dois a seis slots disponíveis para armazenar informações.
Também vimos que quando a memória de trabalho está cheia, ela não consegue pensar corretamente.
Há uma segunda coisa que o cérebro não pode fazer quando a memória de trabalho está muito cheia:
armazenar informações de volta para o LTM. Isso é ilustrado na figura 10.6.
Analisamos dois tipos de carga cognitiva: carga intrínseca causada pelo problema
própria e carga estranha causada pelo fraseado do problema. Mas há um terceiro
tipo de carga cognitiva que não cobrimos: carga cognitiva pertinente.
Carga alemã, que significa algo como carga relevante, é o esforço que seu
cérebro para armazenar informações de volta para o LTM. Quando toda a carga cognitiva que você tem
espaço para está cheio de carga intrínseca e alheia, não há espaço para
carga; em outras palavras, você não consegue se lembrar dos problemas que resolveu e suas
soluções.
Machine Translated by Google

174 CAPÍTULO 10 Melhorando na resolução de problemas complexos

Memória de Memória de
longo prazo
curto prazo

Em formação

Memória
de trabalho

Figura 10.6 A carga alemã é necessária para habilitar a seta destacada e formar a
memória de trabalho no LTM. Se a memória de trabalho estiver trabalhando muito
(ou seja, com muita carga), nenhuma informação poderá ser armazenada.

É por isso que, às vezes, após uma sessão pesada de codificação, você não consegue lembrar o que
fez. Seu cérebro estava tão engajado que não conseguia armazenar as soluções.
Agora que você está familiarizado com os três tipos de carga cognitiva, incluindo a carga relevante,
podemos reconsiderar o experimento australiano com os alunos do nono ano. Agora entendemos por
que o grupo que usou as receitas se saiu melhor em novas equações: como sua carga cognitiva não era
tão alta, eles puderam refletir e lembrar dessas receitas. Eles aprenderam que, ao resolver um problema
de álgebra, pode ser uma boa ideia mover parte da equação para o outro lado do sinal de igual, o que
requer transformar um mais em menos, ou que eles sempre podem dividir ambos os lados pelo mesmo
valores.
O tipo de habilidades que os alunos aprenderam com as receitas são exatamente o tipo de
abordagem que é útil em quase todos os problemas de álgebra e, portanto, o primeiro grupo também
pode aplicá-las às novas equações. O segundo grupo, embora engajado no pensamento profundo,
estava mais focado no problema em questão do que em regras genéricas.

As pessoas que se preocupam em ensinar receitas têm isso ao contrário. Concordo, parece tão
sensato: se queremos que as crianças sejam solucionadoras de problemas, elas devem resolver problemas.
Essa mesma linha de pensamento está presente na comunidade de programação: se você quer ser um
programador melhor, programe muito! Tenha projetos paralelos e experimente as coisas e você
aprenderá. Mas isso parece não ser verdade.
Embora os experimentos de Sweller se concentrem no ensino de matemática, experimentos
semelhantes foram feitos para programação. Esses estudos mostraram resultados semelhantes: as
crianças aprendem mais com programas de leitura, além de uma explicação que os acompanha, do que
com a programação.1 Como diz o psicólogo holandês Paul Kirschner: “Você não se torna um especialista
fazendo coisas de especialista”.

1
Marcia C. Linn e Michael J. Clancy, “O caso para estudos de caso de problemas de programação,”
Comunicações da ACM, vol. 35, não. 3, 1992, https://dl.acm.org/doi/10.1145/131295.131301.
Machine Translated by Google

Resumo 175

10.4.2 Usando exemplos trabalhados em sua vida profissional

Vimos que estudar código explicitamente e estudar o processo de como ele foi criado pode ajudá-lo
a fortalecer suas habilidades de programação. Existem várias fontes que você pode usar ao estudar
código.

COLABORE COM UM COLEGA

Primeiro, você não precisa estudar código sozinho; é mais útil fazê-lo com alguém. Você pode iniciar
um clube de leitura de código no trabalho com outros colegas interessados em estudar código. Se
fizerem isso juntos, será mais fácil manter o hábito de ler o código regularmente (https://code-
reading.org/). Se você estiver em um clube, poderá trocar o código e sua explicação e aprender um
com o outro.
No capítulo 5, abordamos técnicas para entender o código, incluindo fazer resumos de código.
Esses resumos podem servir como explicação para usar ao ler o código.
Você pode estudar seu próprio código e resumos, mas é ainda mais poderoso se você usar um
processo de duas etapas em que primeiro escreve um resumo para o código que escreveu e depois
o troca para aprender com o código de um colega.
EXPLORE
GITHUB Se você está explorando a prática de leitura de código sozinho, felizmente há uma infinidade
de código-fonte e documentação online. O GitHub, por exemplo, é um excelente lugar para começar
a ler código. Basta ir ao código de um repositório que você conhece um pouco, por exemplo, uma
biblioteca que você usa, e ler o código. É melhor se você escolher um repositório no qual o domínio
seja pelo menos um pouco familiar para você, para que não haja muitas palavras e conceitos
desconhecidos causando carga adicional estranha e você possa se concentrar na programação em
si.

LEIA LIVROS OU POSTS DO BLOG SOBRE O CÓDIGO-FONTE

Existem muitos posts de blog que descrevem como as pessoas resolveram um determinado problema
de programação, e estes também podem servir como ferramentas de estudo. E embora não existam
muitos, existem alguns livros que também descrevem o código e sua explicação; por exemplo, os
dois volumes de The Architecture of Open Source Systems , de Amy Brown e Greg Wilson, ou 500
Lines or Less , de Amy Brown e Michael DiBernardo.

Resumo ÿ
Enquanto muitas pessoas em programação argumentam que a resolução de problemas é uma
habilidade genérica, não é. Seu conhecimento prévio de programação, juntamente com o
problema atual que você está resolvendo, influencia a rapidez com que você pode resolver
problemas de programação.
ÿ Seu LTM armazena diferentes tipos de memórias, que desempenham um papel diferente na
resolução de problemas. As duas categorias abrangentes de memórias são memórias implícitas
e explícitas. Memórias implícitas são “memórias musculares”, tarefas que você pode executar
sem pensar nelas, como digitação por toque. Memórias explícitas são memórias das quais
você está ciente e que precisa relembrar ativamente, como a sintaxe de um loop for.
Machine Translated by Google

176 CAPÍTULO 10 Melhorando na resolução de problemas complexos

ÿ Para fortalecer suas memórias implícitas relacionadas à programação, é melhor


automatize habilidades relacionadas, como digitação por toque e memorização de atalhos de
teclado relevantes.

ÿ Para fortalecer suas memórias explícitas relacionadas à programação, estude


código, de preferência com uma explicação sobre como o código foi projetado.
Machine Translated by Google

Parte 4

Ao colaborar no código

Até agora, neste livro, nos concentramos no desenvolvedor individual. Mas na realidade,
software é desenvolvido por equipes. Na parte final deste livro, discutiremos como
entrar no estado de fluxo, onde você pode codificar sem ser interrompido por seus
colegas. Também discutiremos como criar sistemas maiores em que outras pessoas
possam facilmente começar a trabalhar, bem como o processo de integração.
Machine Translated by Google
Machine Translated by Google

O ato de escrever código

Este capítulo abrange


ÿ Comparar diferentes atividades que as pessoas realizam
enquanto interagem com o código
ÿ Examinando como apoiar seu cérebro em
realizar diferentes atividades de forma mais eficaz
ÿ Explorar como as interrupções afetam seu trabalho como
desenvolvedor
ÿ Entender como usar sua memória para melhor
recuperar de uma interrupção

Até agora neste livro, examinamos quais processos cognitivos desempenham um papel quando
leitura e escrita de código. Nos capítulos anteriores, vimos como escrever código
que é mais fácil de ler e como resolver melhor os problemas.
Neste capítulo, direcionamos nossa atenção para longe do código em si e, em vez disso, procuramos
em que processos cognitivos desempenham um papel quando você está executando o ato de
programação. Vamos primeiro examinar o que queremos dizer quando dizemos que alguém está programando.
Vamos mergulhar em diferentes atividades que compõem o ato de programar e explorar
como apoiar melhor essas diferentes atividades.

179
Machine Translated by Google

180 CAPÍTULO 11 O ato de escrever código

Em segundo lugar, examinaremos as implicações cognitivas de uma ocorrência muito temida


na vida dos programadores: interrupções. Investigaremos por que as interrupções enquanto
programação são tão irritantes e examine o que você pode fazer para torná-los menos perturbadores. Ao final
deste capítulo, você estará mais apto a apoiar as atividades de programação e estará mais bem equipado
para lidar com interrupções.

11.1 Diferentes atividades durante a programação


Quando você programa, há muitos tipos diferentes de coisas que você pode estar fazendo.
Essas diferentes atividades foram descritas pela primeira vez pelos pesquisadores britânicos Thomas Green,
Alan Blackwell e Marian Petre (cujo trabalho abordaremos com mais profundidade no capítulo
12) em sua estrutura de notação de dimensões cognitivas (CDN), que avalia a
impacto cognitivo de uma linguagem de programação ou base de código e descreve cinco atividades:
busca, compreensão, transcrição, exploração e incrementação.
A Figura 11.1 fornece uma visão geral das cinco atividades de programação, qual programa
tarefas que você provavelmente faz nessas atividades e o que torna cada atividade difícil.

Tarefa Difícil
Atividade
Executando Codificação Testando Lendo Refatoração
Memória de
Procurando curto prazo
Trabalhando
Compreensão memória
Memória de
Transcrição longo prazo

Incrementação Todos três


Todos três
Exploração

Figura 11.1 Uma visão geral das atividades de programação e os sistemas de memória que eles mais usam

11.1.1 Pesquisando
Pesquisar no código é a atividade em que você está olhando através de uma base de código, pesquisando
para uma informação específica. Esta pode ser a localização precisa de um bug que você precisa
endereçar, todas as chamadas de um determinado método, ou o local em que uma variável deve ser
inicializado.

Ao pesquisar, você estará lendo e executando principalmente código, potencialmente usando


pontos de interrupção e um depurador, ou respondendo em instruções de impressão enquanto executa o
código. A atividade de pesquisa é difícil para o seu STM. Você tem que se lembrar do que você
estavam procurando, quais caminhos no código você já explorou e por quê, e
o que você ainda precisa explorar com mais profundidade. Portanto, esta atividade é melhor suportada
fazendo anotações para descarregar algumas das memórias em papel ou em um documento separado
em seu código. Anote quais informações você está procurando, onde você vai pesquisar
a seguir, e o que você já encontrou.
Conforme discutido anteriormente, às vezes pode ser benéfico fazer mudanças temporárias na
código para facilitar uma determinada tarefa. Ao pesquisar dentro do código, pode ajudar se você deixar pouco
Machine Translated by Google

Diferentes atividades durante a programação 181

breadcrumbs para si mesmo nos comentários indicando por que você visitou o código. Por exemplo,
um comentário como “Eu li este método porque pensei que poderia estar envolvido na inicialização
da classe de página” pode ajudá-lo quando você visitar o código posteriormente na mesma pesquisa.
Isso é especialmente importante quando você suspeita que pode não conseguir terminar a pesquisa
de uma só vez porque uma reunião ou o final do dia de trabalho está se aproximando.
Anotar as etapas da pesquisa ajudará você a concluí-la mais tarde.

11.1.2 Compreensão

Quando você está realizando a atividade de compreensão, você está lendo e executando código
para entender sua funcionalidade. É semelhante à pesquisa, mas com a compreensão, você não tem
uma boa compreensão do que o código faz em detalhes, possivelmente porque é um código que
você escreveu há muito tempo ou porque outra pessoa escreveu o código.
Como vimos no capítulo 5, os desenvolvedores, em média, gastam até 58% de seu tempo
compreendendo o código-fonte existente, portanto, essa atividade é bastante comum no seu dia-a-
dia.
Além de ler e executar o código, a compreensão também pode incluir a execução do código de
teste para obter uma melhor compreensão de como o código deve funcionar.
E como abordamos no capítulo 4, a compreensão também pode incluir a execução de refatorações
para tornar o código mais fácil de entender.
Refatorar o código ajuda porque a compreensão é uma atividade que pesa muito na sua memória
de trabalho. Você tem que raciocinar sobre o código que você ainda não entende completamente.
Como tal, apoiar a memória de trabalho é a melhor estratégia que você tem quando se trata de
compreensão. Tente desenhar um modelo do código e atualizá-lo sempre que aprender novas
informações. Dessa forma, em vez de recuperar informações do seu cérebro, você pode recuperá-
las de uma fonte externa, o que é mais fácil. E ter um modelo à mão também pode ajudar a detectar
equívocos que você possa ter sobre o código. Além disso, se você tiver que terminar a tarefa de
compreensão mais tarde, todas as suas anotações e desenhos podem ajudá-lo a voltar à tarefa com
mais facilidade.

11.1.3 Transcrição

Transcrição é a atividade em que você está “apenas codificando”. Você tem um plano concreto do
que deseja adicionar ou alterar à base de código e fará exatamente isso. Na sua forma mais pura, ao
transcrever você está apenas codificando e nada mais.
A transcrição é difícil principalmente no LTM porque você terá que lembrar de construções
sintáticas para implementação.

11.1.4 Incrementação

A incrementação é uma mistura de busca, compreensão e transcrição. Quando você está


incrementando uma base de código, você está adicionando um novo recurso, que provavelmente
incluirá a pesquisa de local(is) para adicionar código e a compreensão do código existente para
entender onde adicionar código e como fazer isso, seguido por a transcrição real da ideia em sintaxe.
Machine Translated by Google

182 CAPÍTULO 11 O ato de escrever código

Como a incrementação é uma mistura de atividades, também pode ser difícil para os três sistemas de
memória. Assim, as tarefas de incrementação, que são as mais comuns na vida de um programador
profissional, são as que mais necessitam de suporte para os sistemas de memória em
a forma de notas e refatoração para compreensão.
Sua experiência pessoal com a linguagem de programação e a base de código disponível
afeta qual sistema é mais fortemente impactado. Se você conhece a linguagem de programação
bem, seu LTM pode não estar trabalhando muito para lembrar a sintaxe. No outro
Por outro lado, se você conhece bem a base de código, sua memória de trabalho e STM podem não ser
impactado pela busca e compreensão do código.
Mas se a base de código ou a linguagem (ou ambas) for menos familiar, a incrementação
pode ser uma tarefa difícil. Sempre que possível, tente dividir a tarefa de incrementação em
tarefas menores. Ser deliberado sobre qual subtarefa você está fazendo pode ajudá-lo a apoiar
o sistema de memória correto. Diga a si mesmo que você começará pesquisando os
informações, então compreendê-las, seguidas de adicionar o código necessário.

11.1.5 Exploração
A atividade final que Green, Blackwell e Petre discutem é explorar o código. Quando você é
realizando a atividade de exploração, você está essencialmente desenhando com código. Você
pode ter uma vaga ideia de onde você quer ir, mas programando você ganha clareza
sobre o domínio do problema e sobre as construções de programação que você
precisa usar.

Assim como na incrementação, ao explorar você provavelmente está executando várias tarefas diferentes
relacionadas à programação em rápida sucessão: escrever código, executar o código,
executando testes para ver se você está indo na direção certa, lendo o código existente e
potencialmente refatorando o código para torná-lo mais alinhado com seus planos recém-encontrados para ele.
Durante a exploração, é provável que você dependa muito de ferramentas no IDE, por exemplo,
executando testes para ver se pequenas alterações afetaram algum teste, usando refatoração automatizada
ferramentas, ou confiando em “encontrar dependentes” para navegar rapidamente pelo código.

Como a exploração também depende de atividades diferentes, é difícil para os três sistemas de memória,
mas especialmente para a memória de trabalho, porque você está fazendo planos e
projetos em tempo real durante a programação. Embora você possa sentir que documentar sua
planos vão atrapalhar seu fluxo e atrasá-lo, pode ser muito útil fazer algumas
notas aproximadas de sua direção de design ou decisões de design para que você possa liberar
espaço para pensar sobre o problema com mais profundidade.

11.1.6 E quanto à depuração?


Ao discutir essa estrutura com desenvolvedores, eles geralmente se perguntam por que a atividade de
depuração está faltando. A resposta é que quando você está depurando, muitas vezes você está
envolver-se em todas as cinco atividades. A depuração envolve a correção de um bug, mas geralmente também inclui
encontrar a localização do bug antes de corrigi-lo.
Portanto, a depuração geralmente é uma sequência de exploração, pesquisa e compreensão, seguida
pela escrita de código, e pode ser descrita como uma mistura dos cinco
Atividades.
Machine Translated by Google

Programador interrompido 183

EXERCÍCIO 11.1 Durante sua próxima sessão de codificação, reflita sobre as cinco atividades
da estrutura CDN. Qual atividade você está realizando na maior parte do tempo?
Que barreiras você encontrou ao pesquisar, compreender, transcrever, incrementar e explorar?

Atividade Tarefa Tempo gasto

Exploração

Procurando

Compreensão

Transcrição

Incrementação

11.2 Programador interrompido


Muitos programadores hoje em dia trabalham em escritórios abertos, onde as distrações são comuns.
Que tipo de resultados essas interrupções têm em nossos cérebros e em nossa produtividade? Rini van
Solingen, agora professora da Delft University of Technology na
Holanda, estudou interrupções de programadores em meados dos anos 90.
Van Solingen estudou duas organizações diferentes e descobriu
resultados em ambas as organizações. Ele descobriu que as interrupções são comuns e levam de 15 a 20
minutos cada. Cerca de 20% do tempo de um desenvolvedor é gasto em interrupções. Com o
aumento do uso do Slack e de outros aplicativos de mensagens, é provável que hoje em dia as
interrupções sejam mais comuns.1
Há trabalhos mais recentes sobre interrupções também. Chris Parnin, cujo trabalho nós
abordado brevemente no capítulo 3, também estudou as interrupções gravando 10.000 sessões de
programação de 86 programadores. Com este estudo, Parnin confirmou a teoria de van Solingen
resultados, constatando que as interrupções são comuns. De acordo com o estudo de Parnin,2 o
programador médio terá apenas uma sessão ininterrupta de duas horas por dia. Os desenvolvedores
também concordam que as interrupções são um problema. Um estudo da Microsoft revelou que 62% dos
os desenvolvedores consideram a recuperação de interrupções um problema sério.3

11.2.1 As tarefas de programação requerem um aquecimento

No capítulo 9, discutimos os dispositivos fNIRS como uma técnica para medir a carga cognitiva.
Com a ajuda do fNIRS, ganhamos não apenas uma compreensão de que tipo de
código causa carga cognitiva, mas também como a carga cognitiva é distribuída pelas tarefas.

1
Rini van Solingen et al., “Interrupções: Apenas um minuto nunca é”, IEEE Software, vol. 15, não. 5, https://ieeexplore
.ieee.org/document/714843.
2 Chris Parnin e Spencer Rugaber, “Estratégias de retomada para tarefas de programação interrompidas”, https://
ieeexplore.ieee.org/document/5090030.
3
Thomas D. LaToza et al., “Manutenção de modelos mentais: um estudo dos hábitos de trabalho do desenvolvedor”, https://dl.acm
.org/doi/10.1145/1134285.1134355.
Machine Translated by Google

184 CAPÍTULO 11 O ato de escrever código

Em 2014, Takao Nakagawa, pesquisador do Instituto NARA de Ciência e Tecnologia no Japão,


mediu a atividade cerebral com um dispositivo fNIRS.4 Os participantes do estudo foram solicitados
a ler duas versões de um algoritmo escrito em C. Uma versão era um implementação regular,
enquanto o segundo foi deliberadamente complicado pelos pesquisadores. Por exemplo, eles
alteraram os contadores de loop e outros valores de forma que as variáveis fossem atualizadas com
frequência e irregularmente. Essas modificações não alteraram a funcionalidade de um programa.

Nakagawa encontrou dois resultados interessantes. Primeiro, para 9 dos 10 participantes, houve
uma grande variação na carga cognitiva durante a tarefa. A programação não era difícil o tempo
todo; algumas coisas eram difíceis, enquanto outras eram relativamente fáceis. Em segundo lugar,
os pesquisadores também analisaram o momento da tarefa em que ocorreu o maior aumento no
fluxo sanguíneo e, portanto, na carga cognitiva. Seus resultados mostraram que a carga cognitiva no
meio da tarefa foi maior.
Os resultados de Nakagawa podem sugerir que há uma espécie de fase de aquecimento e
resfriamento nas tarefas de compreensão do programa, entre as quais o trabalho mais difícil é
realizado. Os programadores profissionais provavelmente reconhecerão esse tempo de aquecimento
necessário para entrar “na zona” para construir um modelo mental do código e se preparar para
iniciar a atividade de transcrição. Conforme discutido anteriormente neste capítulo, pode ajudar a
delinear ativamente as subatividades em uma tarefa de programação maior.

11.2.2 O que acontece após uma interrupção?


Parnin também analisou o que acontece após uma interrupção e determinou que eles são, sem
surpresa, bastante perturbadores para a produtividade. Leva cerca de um quarto de hora para
começar a editar o código após uma interrupção. Quando interrompido durante a edição de um
método, os programadores podem retomar seu trabalho em menos de um minuto apenas 10% do
tempo.
O que as pessoas fazem para voltar ao código? A partir dos resultados de Parnin, podemos ver
que a memória de trabalho perdeu informações vitais sobre o código em que os programadores
estavam trabalhando. Os programadores em seu estudo precisavam fazer um esforço deliberado
para reconstruir o contexto. Eles frequentemente navegavam para vários locais para reconstruir o
contexto antes de continuar o trabalho de programação. Os participantes do estudo também deixaram
migalhas de pão para si mesmos, por exemplo, inserindo alguns caracteres aleatórios, causando um
erro de compilação e, assim, forçando o que Parnin chamou de lembrete de roadblock: uma maneira
de garantir que o código fosse concluído em vez de permanecer em um estado semi-acabado.
Alguns participantes também usaram uma diferença de fonte entre sua versão atual e a master como
um último recurso para recuperar o estado, mas pode ser complicado encontrar as diferenças reais.

4
Takao Nakagawa et al., “Quantificando a carga de trabalho mental dos programadores durante a compreensão do
programa com base na medição do fluxo sanguíneo cerebral: um experimento controlado”, https://posl.ait.kyushu-
u.ac.jp/ ~kamei/publications/Nakagawa_ ICSENier2014 .pdf.
Machine Translated by Google

Programador interrompido 185

11.2.3 Como se preparar melhor para interrupções


Agora que sabemos que as interrupções são comuns e difíceis 1) Deixe-me

armazenar meu modelo mental.


de recuperar, vamos examinar
Posso te perguntar
2) Claro, eu tenho
o que acontece em seu cérebro quando você é interrompido
algo? prospectivo
com mais profundidade para que possamos nos preparar suporte de memória.

melhor para interrupções. Este capítulo sugere 3) Claro, eu rotulei

três técnicas para ajudar a lidar com interrupções. meus subobjetivos.

GUARDE SEU MODELO MENTAL


No início do livro, discutimos várias tecnologias

técnicas que podem ser usadas para apoiar o trabalho


Figura 11.2 Três técnicas para ajudar na
memória e STM, como fazer anotações, desenhar modelos e
recuperação após uma interrupção
refatorar código para facilitar
o cérebro. Essas técnicas também podem ser úteis para a recuperação de uma interrupção.
Os resultados de Nakagawa mostraram um período de aquecimento nas atividades de compreensão, que é
provavelmente gasto na construção de um modelo mental do código em mãos. Se partes do
model são armazenados separados do código, o que pode ajudá-lo a recuperar rapidamente seu
modelo. Deixar anotações sobre seu modelo mental nos comentários também é útil.
O uso de comentários extensos ganhou uma má reputação entre alguns desenvolvedores porque o código
deveria ser “autodocumentado”, tornando os comentários desnecessários. No entanto, o código raramente
explica os processos de pensamento do programador e, portanto,
na maioria das vezes não representa adequadamente o modelo mental do criador. Nós não somos
acostumado a escrever no código, por exemplo, por que uma determinada abordagem foi escolhida,
quais são os objetivos do código, ou quais alternativas foram consideradas para sua implementação. Quando
esses tipos de decisões não são escritas em nenhum lugar, elas podem, na melhor das hipóteses,
ser implicitamente redescoberto, o que obviamente é um processo demorado. John Ouster Hout descreve isso
muito bem em seu livro The Philosophy of Software Design (Yaknyam Press,
2018): “A ideia geral por trás dos comentários é capturar informações que estavam no
mente do designer, mas não pode ser representado no código.”
Além de ser muito útil para outras pessoas lerem o código, documentar decisões também pode ser útil
para armazenar seu próprio modelo mental temporariamente, tornando mais fácil continuar programando
posteriormente. Em seu livro The Mythical Man-Month
(Addison-Wesley, 1995), Fred Brooks diz que os comentários são mais importantes na
processo de compreensão do programa porque eles estão sempre presentes. Enquanto as notas sobre
papel ou em documentos são úteis, encontrando documentos relevantes quando você começa novamente
pode causar notas mentais extras.

Quando você é interrompido e tem a opção de adiar a interrupção por um


pouco tempo, por exemplo, quando é uma mensagem do Slack ou um colega em sua mesa que
pode esperar um pouco, em vez de um telefone tocando - pode ser muito útil apenas
“despeje o cérebro” seu último modelo mental do código em um comentário. Nem sempre será
ajuda, mas em alguns casos pode.
Machine Translated by Google

186 CAPÍTULO 11 O ato de escrever código

AJUDE A SUA MEMÓRIA PROSPECTIVA


Para a segunda técnica, temos que mergulhar um pouco mais fundo em diferentes tipos de memórias. No
capítulo 10, examinamos dois tipos de memórias: implícitas e explícitas, ou
declarativa e processual.
Há um tipo adicional de memória que diz respeito ao futuro e não ao
passado, chamado memória prospectiva, a memória de lembrar de fazer algo no
futuro. Esse tipo de memória está intimamente relacionado ao planejamento e à resolução de problemas. Quando
você diz a si mesmo que no caminho para casa você tem que se lembrar de pegar um pouco de leite na
a loja, ou quando você se lembra de refatorar um determinado código feio mais tarde,
você está usando sua memória prospectiva.
Como os desenvolvedores suportam sua memória prospectiva tem sido o tema de alguns
estudos. Vários estudos descreveram como os desenvolvedores tentaram lidar com possíveis falhas de memória.
Por exemplo, os desenvolvedores geralmente adicionam comentários “a fazer” no
parte do código em que estão trabalhando para lembrá-los de completar ou melhorar parte
o código.5 É claro que, como a maioria dos programadores experimentou, esses comentários
pode durar muito tempo e muitas vezes permanecer sem solução. A Figura 11.3 mostra uma recente
pesquisa no GitHub, resultando em 136 milhões de resultados de código contendo a palavra “to-do”.

Figura 11.3 Comentários de tarefas podem permanecer nas bases de código sem serem resolvidos.

5
Margaret Ann Storey et al., “TODO or to Bug: Exploring How Task Annotations Play a Role in the Work Practices of
Software Developers,” https://dx.doi.org/doi:10.1145/1368088.1368123.
Machine Translated by Google

Programador interrompido 187

Além de comentários de tarefas e erros de compilação intencionais, os programadores também usam


técnicas que outros funcionários de escritório usam: deixar notas adesivas em sua mesa e enviar e-mails para si
mesmos. Notas em papel e e-mails, é claro, têm a desvantagem de
sendo desconectado da base de código, mas ainda pode ser útil.
Parnin também desenvolveu um plug-in do Visual Studio que permite que você dê suporte à sua memória
prospectiva quando tiver que parar de programar.6 Por exemplo, ele permite que você
adicionar itens de tarefas ao seu código e dar-lhes uma data de validade para que você não se esqueça de
trabalhar nas tarefas.

SUBOBJETIVOS DA ETIQUETA

Uma terceira prática que pode ajudar a proteger contra interrupções é chamada de rotulagem de submetas,
em que você escreve explicitamente em quais pequenos passos um problema pode ser dividido. Por
Por exemplo, ao analisar e reestruturar o texto, essas etapas podem ser

1 Analisar texto e receber árvore de análise.


2 Filtre a árvore de análise.
3 Aplaine a árvore de volta ao formato de texto.

Embora esses passos em si não sejam difíceis de imaginar ou lembrar, quando você
são interrompidos entre eles, pode ser complicado retornar ao código e
lembre-se do que você estava planejando fazer. Se programo uma tarefa maior, tento começar com
escrevendo as etapas no meu código como comentários, assim:

# analisa o texto
# recebe a árvore de
análise # filtra a árvore de análise e
# achata a árvore de volta ao formato de texto

Dessa forma, posso preencher pequenas partes do código e sempre tenho um plano para recorrer.
Um estudo sobre programadores de Lauren Margulieux, professora de tecnologias de aprendizagem em
do Departamento de Ciências da Aprendizagem da Georgia State University, mostrou que quando
você fornece submetas, os programadores as usam para organizar mentalmente a solução.7
Submetas são úteis para organizar seu próprio pensamento após uma interrupção, mas são
também útil em outras situações. Por exemplo, alguns dos subobjetivos colocados no código
podem permanecer como comentários e servir como documentação posteriormente. Eles também podem ser usados
em colaboração onde um programador sênior projeta os subobjetivos e outros programadores implementam partes
de uma solução maior.

11.2.4 Quando interromper um programador


No capítulo 9, abordamos várias maneiras de detectar a carga cognitiva que as pessoas experimentam,
incluindo questionários e métricas baseadas no cérebro. Mas existem outras maneiras de acessar
a carga cognitiva que uma determinada tarefa cria.

6
https://marketplace.visualstudio.com/items?itemName=chrisparnin.attachables.
7
Lauren E. Margulieux et al., “Material instrucional rotulado com submetas melhora o desempenho e a transferência em
Aprendendo a desenvolver aplicativos móveis”, https://doi.org/10.1145/2361276.2361291.
Machine Translated by Google

188 CAPÍTULO 11 O ato de escrever código

Um exemplo é usar uma medida de tarefa dupla. Uma tarefa dupla é uma segunda tarefa que um participante
executa enquanto trabalha na tarefa original. Por exemplo, ao resolver uma equação matemática, em momentos
aleatórios uma letra A aparecerá na tela. Sempre que o A
aparece, o participante precisa clicar no A o mais rápido possível. Com que rapidez e
com precisão um participante pode executar a segunda tarefa é uma boa medida de quanto
carga cognitiva é experimentada. Pesquisadores mostraram que uma medição de dupla tarefa
pode ser uma boa maneira de estimar a carga cognitiva. Há desvantagens em usar um dual
tarefa, também, como você pode imaginar; a segunda tarefa em si também pode adicionar carga cognitiva,
interferindo na tarefa original.
Usando uma medição de tarefa dupla, a pesquisa explorou a conexão entre
carga cognitiva e interrupções. Brian P. Bailey, professor de ciência da computação na
da Universidade de Illinois, dedicou grande parte de sua carreira à compreensão do
causa e efeito das interrupções.
Em um estudo de 2001,8 50 participantes foram solicitados a realizar uma tarefa primária enquanto
sendo interrompido. O experimento utilizou diferentes tipos de tarefas, desde contar o
ocorrência de palavras em uma grade para ler algum texto e responder perguntas sobre o
texto. Ao executar essas tarefas primárias, os participantes foram interrompidos por
informações como manchetes de notícias de última hora e atualizações do mercado de ações. Este estudo
foi executado como um experimento controlado, com dois grupos. Um grupo recebeu as interrupções durante
uma tarefa primária, enquanto o outro grupo as recebeu após completar
uma tarefa.

Embora os resultados de Bailey não tenham sido surpreendentes, eles oferecem uma visão mais profunda
das interrupções. Bailey descobriu que leva mais tempo para concluir uma tarefa interrompida do que uma tarefa
não interrompida (sem contar o tempo da interrupção real) e que
as pessoas percebem que tarefas interrompidas são mais difíceis de completar do que uma tarefa não
interrompida.
Bailey não mediu apenas o tempo e a dificuldade das tarefas; também investigou o
estado emocional dos participantes. Os participantes responderam a perguntas sobre seu aborrecimento
e níveis de ansiedade cada vez que uma tarefa de interrupção aparecia. Os resultados desses
questões mostraram que o nível de incômodo experimentado é influenciado quando o
tarefa de interrupção é exibida. Participantes do grupo onde as interrupções
apareceram durante a tarefa primária ficaram mais irritados do que no grupo que viu o
interrupções após uma tarefa. O mesmo acontecia com a ansiedade. Os participantes cuja principal
tarefa foi interrompida experimentou mais ansiedade. Um estudo de acompanhamento de 2006 que utilizou um
configuração muito semelhante revelou que pessoas interrompidas também cometem o dobro de erros.9
Com base nesses resultados, pudemos concluir que pode ajudar os programadores, que
será inevitavelmente interrompido de vez em quando, para ser interrompido em momentos mais convenientes

8
Brian P. Bailey et al., “Os Efeitos das Interrupções no Desempenho da Tarefa, Aborrecimento e Ansiedade no Usuário
Interface,” http://mng.bz/G6KJ.
9
Brian P. Bailey, “Sobre a Necessidade de Sistemas de Atenção à Atenção: Medindo os Efeitos das Interrupções no
Desempenho da Tarefa, Taxa de Erros e Estado Afetivo”, Computers in Human Behavior, vo. 22, não. 4, pp. 685-708, 2006,
http://mng.bz/zGAA.
Machine Translated by Google

Programador interrompido 189

vezes (por exemplo, depois de ter completado uma tarefa). Com base nessa ideia, Manuela Züger,
então estudante de doutorado na universidade de Zurique, desenvolveu uma luz interativa chamada
FlowLight.
O FlowLight (https://emea.embrava.com/pages/flow) é uma luz física que um desenvolvedor
pode colocar em sua mesa ou em cima de sua tela. Com base nas interações do computador, como
velocidades de digitação e cliques do mouse, o FlowLight detecta se o programador está
profundamente engajado em uma tarefa e experimentando alta carga cognitiva. Coloquialmente, esse
estado às vezes também é chamado de “no fluxo” ou “na zona”. Quando o programador está muito
na zona e não deve ser interrompido, o FlowLight piscará em vermelho. Com um pouco menos de
atividade, o FlowLight brilhará continuamente em vermelho. Se um desenvolvedor parecer disponível,
a luz ficará verde.
Züger experimentou o FlowLight em um estudo de campo em larga escala com mais de 400
participantes de 12 países e descobriu que o FlowLight pode reduzir as interrupções em 46%.
Muitos participantes do estudo continuaram usando o FlowLight após o término do experimento, e o
FlowLight agora está disponível como um produto comercial.10

11.2.5 Alguns pensamentos sobre multitarefa


Ao ler sobre interrupções, você pode ter refletido sobre o conceito de multitarefa. As interrupções
são tão ruins assim? Nosso cérebro não pode fazer várias coisas ao mesmo tempo, como um
processador multicore?
MULTITAREFAS E AUTOMATIZAÇÕES

Infelizmente, há evidências contundentes de que as pessoas não podem realizar multitarefas


enquanto realizam tarefas cognitivas profundas. Isso pode não ser tão convincente, porque você
pode estar lendo este livro enquanto ouve música ou ouvindo este livro enquanto corre ou tricota,
então como posso dizer que uma pessoa não pode fazer duas coisas ao mesmo tempo?
Lembre-se das três fases de armazenamento de informações: cognitiva, associativa e autônoma.
Vamos dizer de outra forma: você não pode fazer duas ou mais tarefas ao mesmo tempo quando não
atingiu a fase autônoma para elas. Ler inglês provavelmente não é algo que você ainda precisa
aprender, então você pode fazê-lo enquanto se envolve em outras tarefas que você também
automatizou, como tricô. Às vezes, porém, quando você está lendo uma passagem que é
especialmente difícil porque contém muitas ideias novas, você sente a necessidade de diminuir o
volume da música para poder se concentrar melhor. Este é o seu cérebro dizendo a si mesmo que
você não pode ser multitarefa, e é por isso que as pessoas também se sentem compelidas a desligar
o rádio quando estacionam o carro.
Se você não acredita em seu próprio cérebro, existe alguma ciência que mostra multitarefa
não funciona tão bem quanto você imagina.

10Manuela Züger, “Reducing Interruptions at Work: A Large Scale Field Study of FlowLight,” https://
www.zora.uzh.ch/id/eprint/136997/1/FlowLight.pdf .
Machine Translated by Google

190 CAPÍTULO 11 O ato de escrever código

PESQUISA SOBRE MULTITAREFAS

Em 2009, Annie Beth Fox, agora professora de métodos quantitativos no Massachusetts


General Hospital's Institute of Health Professions,11 comparou estudantes que
lendo um texto enquanto também usa um mensageiro instantâneo para alunos que estavam totalmente
focado no texto. Enquanto ambos os grupos entenderam o texto igualmente bem, o grupo
que foi interrompido por mensagens precisou de cerca de 50% a mais de tempo tanto para ler o
texto e respondendo a perguntas sobre ele posteriormente.
O psicólogo holandês Paul Kirschner realizou um estudo em 2010 com cerca de 200 alunos no qual
perguntou sobre seus hábitos no Facebook. Usuários pesados do Facebook
estudaram tanto quanto os não usuários, mas os usuários pesados tiveram uma média de notas significativamente
mais baixa. Isso foi especialmente verdadeiro para os alunos que relataram responder às mensagens
imediatamente após entrarem. O interessante é que as pessoas que realizam várias tarefas muitas vezes se sentem
muito produtivo.12
Em um experimento controlado em que os alunos estavam realizando uma tarefa enquanto também se
comunicavam com um parceiro usando mensagens on-line, os próprios alunos descobriram que
desempenho satisfatório, mas seus parceiros deram a eles uma classificação muito mais baixa. Isto é
provavelmente significa que programar enquanto conversa no Slack pode não ser o melhor
maneira de fazer o trabalho.13

Resumo
ÿ Quando você está programando, você executa uma combinação de diferentes atividades de programação:
pesquisa, compreensão, transcrição, incremento e
exploração. Cada atividade exerce pressão sobre diferentes sistemas de memória. Portanto, cada
atividade deve ser apoiada por diferentes técnicas.
ÿ As interrupções durante a programação não são apenas irritantes; são prejudiciais à produtividade porque
leva tempo para reconstruir seu modelo mental de
o código.

ÿ Para lidar melhor com interrupções, transfira modelos mentais em notas, documentos
tação ou comentários.

ÿ Apoie deliberadamente sua memória prospectiva se você não conseguir completar uma tarefa
documentar seus planos.
ÿ Tente limitar as interrupções aos momentos em que você experimenta baixa carga cognitiva, por
exemplo, através da automação usando um FlowLight ou manualmente, definindo seu
status no Slack.

11Annie Beth Fox et al., “Distrações, distrações: as mensagens instantâneas afetam o desempenho dos estudantes
universitários em uma tarefa de compreensão de leitura simultânea? Ciberpsicologia e Comportamento, vol. 12, pp. 51–53, 2009.
https://doi.org/10.1089/cpb.2008.0107.
12Paul A. Kirschner e Aryn C. Karpinski, “Facebook® and Academic Performance,” Computers in Human Behav ior, vol.
26, não. 6, pp. 1237-1245, 2010, http://mng.bz/jBze.
13LingBei Xu, “Impact of Simultaneous Collaborative Multitasking on Communication Performance and
Experience, 2008, http://mng.bz/EVNR.
Machine Translated by Google

Projetando e
melhorar sistemas maiores

Este capítulo abrange


ÿ Examinar o efeito que diferentes decisões de design têm
na compreensão das bases de código
ÿ Explorar trade-offs entre diferentes decisões de
design
ÿ Melhorar o design das bases de código existentes para
melhor processamento cognitivo

Até agora, neste livro, discutimos a melhor maneira de ler e escrever código. Para isso, nós
examinaram como os processos cognitivos desempenham um papel ao ler e escrever código.
Para bases de código maiores, no entanto, não são apenas pequenas partes do código que influenciam como
é fácil para as pessoas compreendê-lo. A maneira como você organiza o código também
influencia muito a facilidade com que outras pessoas podem interagir com o código. Isso é especialmente
verdadeiro para código em bibliotecas, frameworks e módulos que outros programadores
usar em vez de mudar.
Muitas vezes, quando falamos sobre bibliotecas, frameworks e módulos, falamos sobre
seus aspectos técnicos, como a linguagem em que são criados. No entanto, as bases de código

191
Machine Translated by Google

192 CAPÍTULO 12 Projetando e aprimorando sistemas maiores

também pode ser visto através de uma lente cognitiva. Neste capítulo, discutiremos a CDN, que é
uma técnica para examinar as bases de código de uma perspectiva cognitiva. A CDN ajuda você a
responder a perguntas sobre grandes bases de código existentes, como “Este código será fácil para
as pessoas mudarem?” ou “Esta base de código será fácil para as pessoas encontrarem informações?”
Examinar as bases de código de uma perspectiva cognitiva em vez de técnica pode ajudá-lo a obter
uma melhor perspectiva de como as pessoas interagem com seu código.
Depois de discutirmos a CDN e estudarmos como ela pode apoiar nossa compreensão das
bases de código, veremos como usá-la para melhorar o design das bases de código existentes
usando uma estrutura adaptada chamada dimensões cognitivas das bases de código (CDCB).
No capítulo anterior descrevemos cinco atividades de programação diferentes. Este capítulo
também examinará como as propriedades de uma base de código influenciam as diferentes
atividades de programação de diferentes maneiras.

12.1 Examinando as propriedades das bases de código


Muitas vezes, quando falamos de bibliotecas, frameworks e módulos, falamos de seus aspectos
técnicos. Costumamos dizer coisas como “Esta biblioteca é escrita em Python”,
“Esta estrutura usa node.js” ou “Este módulo é pré-compilado”.
Quando discutimos linguagens de programação, muitas vezes também olhamos para o domínio
técnico, por exemplo, seu paradigma (orientado a objetos, funcional ou uma mistura de ambos),
talvez a existência de um sistema de tipos ou se a linguagem é compilada para código de byte ou
interpretado por um programa em outra linguagem. Você também pode ver onde a linguagem,
estrutura ou biblioteca pode ser executada; por exemplo, este programa é executado no navegador
ou em uma máquina virtual? Todos esses aspectos dizem respeito ao domínio técnico (ou seja, o
que a linguagem pode fazer).
No entanto, quando discutimos diferentes bibliotecas, estruturas, módulos ou linguagens de
programação, também podemos discutir o que eles fazem com seu cérebro e não com seu computador.

EXERCÍCIO 12.1 Pense em uma base de código que você usou recentemente e que você
mesmo não escreveu. Pode ser uma biblioteca que você usa, da qual você teve que ler o
código para entender como chamar uma função, ou uma estrutura na qual você corrigiu um bug.

Agora considere estas questões:

ÿ O que facilitou a execução de sua tarefa (por exemplo, a presença de documentos


menção, bons nomes de variáveis, comentários)?
ÿ O que dificultou a execução de sua tarefa (por exemplo, código complexo ou falta de
documentação)?

12.1.1 Dimensões cognitivas


A CDN pode ser usada para avaliar a usabilidade de grandes bases de código existentes. Ele foi
originalmente criado pelos pesquisadores britânicos Thomas Green, Alan Blackwell e Marian Petre
e consiste em várias dimensões diferentes, cada uma representando uma maneira diferente de
examinar a base de código disponível. As dimensões foram originalmente criadas para examinar
visualizações, como fluxogramas, e posteriormente aplicadas a linguagens de programação,
Machine Translated by Google

Examinando as propriedades das bases de código 193

também, daí o nome. Linguagens de programação, como fluxogramas, podem ser vistas como notações, uma
forma de expressar pensamentos e ideias.
As dimensões como Green, Blackwell e Petre as descrevem aplicam-se apenas a notações, mas neste livro
generalizamos o uso de CDN para bases de código em vez de linguagens de programação. Chamamos essa
versão de dimensões cognitivas de bases de código (CDCB)
e usá-lo para examinar uma base de código para entender como ele pode ser entendido e

melhorado. CDCB é especialmente útil para código escrito em bibliotecas e frameworks,


que outros programadores costumam chamar em vez de adaptar.
Vamos primeiro discutir cada uma das dimensões isoladamente e, em seguida, mergulhar em como o

diferentes dimensões interagem umas com as outras e como podemos usá-las para melhorar
bases de código existentes.

PROPENSÕES A ERROS

A primeira dimensão a ser discutida é chamada de propensão ao erro. Em algumas linguagens de programação
é mais fácil cometer um erro do que em outras linguagens. JavaScript está atualmente
uma das linguagens mais populares, mas é conhecida por ter alguns cantos excêntricos
casos.

Em JavaScript e outras linguagens de tipagem dinâmica, as variáveis não são inicializadas


com um tipo quando são criados. Porque não está claro qual é o tipo de um objeto
runtime, os programadores podem ficar confusos sobre os tipos de variáveis que são
levando a erros. Além disso, a coerção inesperada de uma variável de um tipo em outro
tipo pode causar erros. Linguagens que possuem sistemas de tipos fortes, como Haskell, são
pensado como sendo menos propenso a erros porque o sistema de tipos fornecerá orientação
ao codificar.
Bases de código, em vez de linguagens de programação, também podem ser propensas a erros, para exame
por causa de convenções inconsistentes, falta de documentação ou nomes vagos.
Às vezes, as bases de código herdam dimensões da linguagem de programação que
Por exemplo, um módulo escrito em Python pode ser mais propenso a erros do que uma biblioteca muito
semelhante escrita em C porque o Python não tem um sistema de tipos tão forte quanto o C para detectar erros.

OS SISTEMAS DE TIPO PREVEM ERROS Você pode se perguntar se é verdade que


sistemas de tipo evitam erros. Em um extenso conjunto de experimentos comparando
Java para Groovy, o pesquisador alemão Stefan Hanenberg demonstrou esse tipo
sistemas podem de fato ajudar os programadores a localizar e corrigir erros mais rapidamente. Dentro
muitos casos nos experimentos de Hanenberg, o lugar no código onde o
compilador apontou que um erro era o mesmo de onde o código falharia
tempo de execução. A execução de código, é claro, leva mais tempo e, como tal, contar com
erros em tempo de execução é geralmente mais lento.

Hanenberg tentou vários métodos para melhorar o código digitado dinamicamente para
torná-lo menos propenso a erros, incluindo melhor suporte e documentação IDE,
mas mesmo nessas situações, os sistemas do tipo estático superaram os dinâmicos
em termos de tempo e precisão dos programadores encontrando bugs.
Machine Translated by Google

194 CAPÍTULO 12 Projetando e aprimorando sistemas maiores

CONSISTÊNCIA
Outra maneira de examinar como as pessoas irão interagir com uma linguagem de programação ou base
de código é a consistência: quão semelhantes são as coisas? Os nomes são sempre estruturados da
mesma maneira, por exemplo, usando os mesmos moldes de nome que discutimos no capítulo 8? O
layout dos arquivos de código é semelhante para classes diferentes?
Um exemplo de situação em que muitas linguagens de programação apresentam consistência é na
definição de funções. Talvez você nunca tenha pensado nisso, mas as funções internas normalmente
têm a mesma interface do usuário que as funções definidas pelo usuário. Quando você está olhando
para uma chamada de função como print() ou print_customer(), você não pode ver na chamada quem
criou a função, o criador da linguagem de programação ou o criador do código.
Uma estrutura ou linguagem que é inconsistente no uso de nomes e convenções pode levar a uma
maior carga cognitiva, porque levará mais energia ao seu cérebro para entender o que é o quê, e pode
levar mais tempo para encontrar informações relevantes.
A consistência está relacionada à propensão a erros, como vimos no capítulo 9. Códigos nos quais
ocorrem antipadrões linguísticos (por exemplo, nomes que não correspondem à implementação do
código) são mais propensos a erros e causam maior carga cognitiva.

DIFUSÃO
Em um capítulo anterior, abordamos os cheiros de código, que podem tornar o código mais difícil de ler.
Um cheiro de código bem conhecido é um método longo, no qual um método de uma função consiste em
muitas linhas de código, tornando-o mais difícil de entender.
Um método longo pode ser culpa do programador se ele adicionar complexidade desnecessária a
um método ou tentar encaixar muita funcionalidade em um método. No entanto, algumas linguagens de
programação também precisam de mais espaço do que outras para a mesma funcionalidade.
A difusão da dimensão cobre isso. A difusão refere-se a quanto espaço ou espaço uma construção de
programação ocupa.
Por exemplo, um loop for em Python se parece com isso:

para i no intervalo(10): print(i)

Em C++, o mesmo código seria

for (i=0; i<10; i++){


cout << i;
}
para i no intervalo (10): imprima
Simplesmente contando as linhas de código, C++ tem três linhas, (i)

enquanto Python tem duas. No entanto, a difusão não diz respeito


apenas ao número de linhas de código; você também pode
for (i=0; i<10; i++) {
considerar em quantos pedaços o código consiste. Se você contar cout << i;
os elementos individuais, que podem ser fragmentados se for }
iniciante, o código Python terá sete elementos, enquanto o código
Figura 12.1 Partes diferentes
C++ conterá nove, conforme ilustrado na figura 12.1. em um loop for simples Python
(superior) e C++ (inferior)
Machine Translated by Google

Examinando as propriedades das bases de código 195

Essa diferença entre o número de pedaços está no fato de que existem elementos

no código C++ que não estão presentes na versão Python (por exemplo, i++).
Nós também podemos ter versões mais e menos difusas do mesmo código dentro da mesma linguagem
de programação. Cobrimos o exemplo de compreensões de lista em Python nos capítulos anteriores; aqui
novamente estão dois trechos de código Python que fazem a mesma coisa:

california_braches = []
para filial em filiais:
if branch.zipcode[0] == '9'
california_branches.append(branch)

california_branches = [b para b em ramificações se b.zipcode[0] == '9']

A segunda versão do código é menos difusa, o que pode afetar a legibilidade e a compreensão.

DEPENDÊNCIAS OCULTAS
A dimensão de dependências ocultas indica até que ponto as dependências são visíveis para o usuário. Um
exemplo de sistema com altas dependências ocultas é uma página HTML com um botão controlado por
JavaScript, que é armazenado em um arquivo diferente. Pode ser difícil ver no arquivo JavaScript quais páginas
HTML chamam a função em tal situação.
Outro exemplo são os requisitos para arquivos separados dos arquivos de código. A partir da base de código,
pode ser difícil ver todas as bibliotecas e estruturas que precisam ser instaladas para que o código seja
executado corretamente.
Em geral, no código, as funções que estão sendo chamadas dentro de outra função ou classe são mais
visíveis do que o contrário: quais funções ou classes chamam uma determinada função. No primeiro caso,
sempre podemos ler o texto de uma função e ver quais funções estão sendo chamadas no corpo.

Embora os IDEs modernos possam revelar dependências ocultas, como mostrado na figura 12.2, encontre
A configuração das dependências ainda requer o uso de cliques do mouse ou atalhos.

Figura 12.2 Opção no PyCharm para encontrar todos os locais de chamada de uma determinada função
Machine Translated by Google

196 CAPÍTULO 12 Projetando e aprimorando sistemas maiores

Os criadores de código podem compensar dependências ocultas com documentação mais extensa. As equipes podem
considerar ter uma política para discutir e adotar novas
dependências, bem como documentá-las quando a adoção ocorrer.

PROVISIONALIDADE

A dimensão da provisoriedade descreve como é fácil pensar ao usar a ferramenta. Como


abordamos no capítulo 11, às vezes você está programando de forma exploratória
quando você ainda não tem certeza do que exatamente você está criando. Quando você está explorando, você
pode usar caneta e papel ou quadros brancos. Estas são ferramentas que têm a máxima provisionalidade porque você
pode esboçar livremente, escrever todos os tipos de anotações e escrever
código incompleto ou errado sem problemas.
Quando começamos a codificar em uma base de código, no entanto, perdemos alguma liberdade. Se escrevermos
código que possui erros de sintaxe, não pode ser verificado por tipo e, se não for verificado,
o código não pode ser executado. Embora seja útil ter esses tipos de verificações, eles
pode prejudicar nossa capacidade de experimentar coisas e usar o código como um meio de pensamento, em vez de
um modelo de execução.

Se uma base de código ou linguagem de programação for muito restrita (por exemplo, usando tipos,
afirmações e pós-condições), pode ser difícil usar código para expressar um pensamento. Nós
então diga que essa ferramenta tem baixa provisoriedade.
A provisoriedade é um fator essencial na capacidade de aprendizado porque expressar ideias vagas
e código incompleto pode ser necessário se você for iniciante em um determinado sistema.
Pensar em um plano para seu código enquanto pensa em tipos e sintaxe pode causar
muita carga cognitiva em iniciantes.

VISCOSIDADE

Relacionada à provisoriedade está a viscosidade: quão difícil é fazer mudanças em um determinado sistema.
Normalmente, as bases de código escritas em linguagens tipadas dinamicamente são um pouco mais fáceis de mudar.
Você pode simplesmente alterar o código e não precisa alterar todas as definições de tipo correspondentes. O código
que não é muito modular e contém grandes blocos de código também pode ser mais fácil de alterar porque pode ser
alterado diretamente e você não precisa fazer
alterações em vários lugares para várias funções ou classes.
A facilidade de mudança de um sistema depende não apenas da linguagem de programação e da própria base
de código; fatores que cercam a base de código também afetam a viscosidade.
Por exemplo, se levar muito tempo para uma base de código compilar ou executar testes, isso aumenta
viscosidade de cada mudança.

AVALIAÇÃO PROGRESSIVA

Uma dimensão relacionada à provisoriedade é a avaliação progressiva. A dimensão da avaliação progressiva descreve
quão fácil é em um determinado sistema verificar ou executar um trabalho parcial. Como vimos, um sistema com muita
provisoriedade permite que o usuário esboce
ideias incompletas. Um sistema com avaliação progressiva permite ao usuário também executar códigos incompletos
ou imperfeitos.
Machine Translated by Google

Examinando as propriedades das bases de código 197

Alguns sistemas de programação permitem que os programadores façam programação ao vivo: o


programador pode alterar e depois executar novamente o código sem interromper a execução do código. Um
exemplo de tal sistema de programação é Smalltalk.
Smalltalk foi a primeira ferramenta de linguagem para suportar programação ao vivo e permitiu
inspeção em tempo real e alterações de código durante a execução do código. Scratch, uma linguagem de
programação para crianças fortemente inspirada em Smalltalk, também permite que as crianças mudem
código sem recompilar.
Ao projetar uma base de código ou biblioteca, você também pode permitir que os usuários executem código parcial
e obter uma visão sobre ele. Um exemplo de um design que permite uma avaliação progressiva
é o uso de parâmetros opcionais. Quando uma função tem parâmetros opcionais, um usuário de
a biblioteca pode primeiro compilar e executar o código com os valores padrão e, em seguida, atualizar
os parâmetros um por um enquanto o sistema está em um estado de funcionamento em cada etapa do caminho.
Outro exemplo é o sistema de furos do Idris, que permite executar código parcial, após o qual o
compilador sugere soluções válidas que podem se encaixar no buraco. Você pode então iterar e refinar
seus tipos, o que leva a buracos menores, e o compilador se torna uma ferramenta para
explorar a solução em vez de uma restrição que bloqueia a exploração.
Um sistema com avaliação menos progressiva não permite que o usuário execute código em um
estado menos completo ou perfeito, o que pode inibir a provisoriedade.

EXPRESSIVIDADE DO PAPEL

A dimensão da expressividade do papel indica quão fácil é ver o papel de diferentes


partes de código em um programa. Um exemplo simples de expressividade de papéis é o fato de que em
quase todas as linguagens de programação, as chamadas de funções sem parâmetros ainda são escritas com
dois colchetes no final; por exemplo, arquivo.open(). Enquanto os projetistas de linguagem poderiam ter decidido
que os usuários podem omitir os colchetes, o
colchetes agora indicam que open() é uma função. Os colchetes no final de uma função são
um exemplo de expressividade do papel.
Outro exemplo famoso de expressividade de papéis é o realce de sintaxe. Muitos IDEs
variáveis de cores de forma diferente das palavras-chave, o que também ajuda você a ver as funções que os diferentes
elementos de código desempenham em um programa.
A expressividade do papel também pode ser alcançada com a sintaxe. Por exemplo, chamar uma função
que retorna um valor booleano is_set em vez de set ajuda o leitor a entender o papel da variável.

Vimos um conceito semelhante no capítulo 9, em antipadrões linguísticos. Quando uma base de código
sofre de antipadrões linguísticos, construções como funções e métodos enganam o
leitor sobre seu papel. Isso significa que a base de código tem menor expressividade de papel e
pode ser mais difícil de entender.

PROXIMIDADE DO MAPEAMENTO

A proximidade da dimensão do mapeamento significa quão perto a linguagem de programação ou


o código é para o domínio em que os problemas são resolvidos. Algumas linguagens de programação têm uma
boa proximidade de mapeamento. A partir do capítulo 1, vimos a linguagem de programação APL, mostrada
novamente na próxima listagem. Enquanto você pode ter
Machine Translated by Google

198 CAPÍTULO 12 Projetando e aprimorando sistemas maiores

pensava que APL era uma linguagem muito confusa, ela, de fato, tem uma grande proximidade do ping de
mapeamento com o domínio do cálculo vetorial.

Listagem 12.1 Representação binária no APL

22222ÿn
Um programa convertendo o número n em representação binária em
APL. A confusão aqui está no fato de que você pode não saber o que ÿ significa.

Por exemplo, todas as variáveis são, por padrão, vetores, como podemos ver na listagem 12.1 do
fato de que T funciona na lista de 2s. Este design é bom se você está acostumado a pensar em vetores e se os
problemas que você está resolvendo podem ser resolvidos com o uso de cálculo vetorial. COBOL também é
frequentemente nomeado como uma linguagem com uma boa proximidade de mapeamento para
domínio dos negócios e das finanças. O Excel é outro exemplo de linguagem de programação com uma boa
aproximação de mapeamento. O layout em linhas e colunas é precisamente
como os cálculos financeiros eram feitos antes mesmo de termos computadores.
A maioria das linguagens de programação modernas, incluindo Java, Python e JavaScript, não
não ter uma boa proximidade de mapeamento; não há problemas com os quais não possamos resolver
essas línguas. Claro que nem sempre isso é ruim. Pode ser muito útil ser
capaz de resolver qualquer problema com Python ou Java, e não ter que aprender um novo
linguagem de programação para cada novo projeto ou cliente.
As bases de código também podem ter uma boa proximidade de mapeamento para seu domínio de negócios.
Bases de código que reutilizam conceitos e palavras de seu domínio de destino são normalmente mais fáceis de
entender para os clientes do que bases de código que usam termos mais genéricos. Por exemplo,
um método chamado executeQuery() tem uma proximidade de mapeamento menor do que a função
encontrarClientes().
Em nosso campo, nos últimos anos, temos visto um interesse crescente em incorporar melhor o domínio no
código. Por exemplo, a filosofia de design orientado ao domínio prescreve que a estrutura e os identificadores no
código devem corresponder ao domínio de negócios.

Este é um movimento em direção a uma melhor proximidade de mapeamento em bases de código.

EXERCÍCIO 12.2 Faça uma lista de todos os nomes de variáveis, funções e classes em seu
base de código. Para cada um dos nomes, investigue a proximidade do mapeamento. Você
pode fazer a si mesmo estas perguntas para cada nome de identificador:

ÿ O nome da variável está expresso no idioma do domínio?


ÿ Está claro a qual processo fora do código esse nome se refere?
ÿ Está claro a qual objeto fora do código esse nome se refere?
OPERAÇÕES MENTAIS DURAS

Alguns sistemas exigem que o usuário pense muito, execute operações mentais difíceis fora
do sistema. Por exemplo, uma linguagem como Haskell exige que o usuário pense em tipos
de todas as funções e parâmetros. Você não pode ignorar as assinaturas de tipo de funções ou isso
ser quase impossível escrever código de trabalho em Haskell. Da mesma forma, C++ requer que o usuário
usar ponteiros em muitas situações e raciocinar com eles em vez de objetos.
Machine Translated by Google

Examinando as propriedades das bases de código 199

É claro que as operações mentais difíceis não são de todo ruins. O pensamento que você precisa de um
usuário fazer pode compensar, por exemplo, em menos erros em um sistema de tipo estrito ou em melhor
desempenho ou uso de memória mais eficiente em ponteiros.
No entanto, quando você pede ao usuário para realizar essas operações mentais difíceis em um sistema
que você projeta, você deve estar ciente disso e considerar as operações com grande
Cuidado.

Exemplos de operações mentais difíceis que as pessoas podem realizar dentro de uma base de código são
muitas vezes situações que exigem muito da memória dos usuários. Por exemplo, pedir aos usuários que
memorizar um grande número de parâmetros para chamar na ordem certa é uma tarefa mental difícil
operação porque coloca uma grande demanda em STM.
Vimos que nomes de funções vagas têm pouca proximidade de mapeamento. Esses nomes
também criar trabalho mental árduo. Ter que memorizar nomes não informativos de funções como execute()
ou control() requer que essas funções sejam armazenadas na memória do usuário.
LTM e, portanto, também podem ser operações mentais difíceis.
Finalmente, algumas operações são difíceis porque prejudicam a memória de trabalho,
por exemplo, se os dados tiverem que ser baixados de duas fontes diferentes em dois formatos diferentes e
convertidos em um terceiro formato. O usuário terá então que acompanhar
dos diferentes fluxos e seus tipos correspondentes.

NOTAÇÃO SECUNDÁRIA

A dimensão de notação secundária indica a possibilidade do programador adicionar


significado extra ao código, que não está na especificação formal. O mais comum
exemplo ocorrendo de notação secundária é a possibilidade de adicionar comentários à fonte
código. Os comentários não são formalmente parte da linguagem, pelo menos não no sentido de que
eles mudam o comportamento do programa. No entanto, os comentários podem ajudar os leitores de
código entendê-lo melhor. Outro exemplo de notação secundária são os parâmetros nomeados em Python.
Conforme mostrado na listagem a seguir, os argumentos podem ser passados junto com um
nome e, nesse caso, a ordem dos parâmetros pode ser diferente no local da chamada
do que na definição da função.

Listagem 12.2 Parâmetros de palavra-chave (nomeados) em Python

def move_arm(ângulo, potência):


robotapi.move(ângulo,potência)
# três maneiras diferentes de chamar move_arm
mover (90, 100) Programa Python demonstrando três maneiras diferentes de
mover (ângulo = 90, potência = 100) chamar uma função: com os argumentos em ordem, com
mover (potência = 100, ângulo = 90) nomes em ordem ou com nomes em qualquer ordem.

Adicionar um parâmetro nomeado a uma chamada de função em Python não altera a maneira como o
código é executado, mas permite que o IDE expresse a função de cada parâmetro
quando a função é chamada.
Machine Translated by Google

200 CAPÍTULO 12 Projetando e aprimorando sistemas maiores

ABSTRAÇÃO

A dimensão de abstração descreve se um usuário do seu sistema pode criar suas próprias abstrações que
são tão poderosas quanto as abstrações internas. Um exemplo de abstrações que a maioria das linguagens
de programação permite é a criação de funções, objetos ou classes. Os programadores podem criar funções,
que são em muitos aspectos semelhantes às funções internas. As funções definidas pelo usuário podem ter
parâmetros de entrada e saída e funcionar da mesma forma que as funções normais. O fato de que os usuários
podem criar funções significa que os usuários podem moldar a linguagem com seus próprios blocos de
construção e adicionar suas próprias abstrações. Embora o poder de fazer suas próprias abstrações esteja
agora disponível em quase todas as linguagens, muitos programadores hoje nunca trabalharam em sistemas
de programação pré-estruturados como assembly ou algum dialeto BASIC onde tais mecanismos de abstração
não estavam disponíveis.

Bibliotecas e frameworks também podem oferecer a seus usuários a opção de criar suas próprias
abstrações. Por exemplo, permitir que um usuário de biblioteca crie uma subclasse à qual funcionalidade
adicional pode ser adicionada tem mais poder de abstração do que uma biblioteca que permite apenas
chamadas de API.
VISIBILIDADE

A visibilidade indica como é fácil ver as diferentes partes de um sistema. Em uma base de código, pode ser
difícil ver em quais classes a base de código consiste, especialmente se o código estiver dividido em arquivos
diferentes.

Bibliotecas ou frameworks também podem oferecer a seus usuários diferentes níveis de visibilidade. Por
exemplo, uma API que busca dados pode retornar uma string, um arquivo JSON ou um objeto, cada um com
uma visibilidade diferente. Se uma string for retornada, é mais difícil ver a forma dos dados e a estrutura
oferece menor visibilidade ao usuário.

12.1.2 Usando o CDCB para melhorar sua base de código

Examinamos diferentes dimensões que os programas podem ter. Essas diferenças podem impactar muito a
forma como as pessoas interagem com uma base de código. Por exemplo, se uma base de código tiver uma
alta viscosidade, os futuros desenvolvedores que trabalham na base de código podem relutar em fazer
alterações. Isso pode levar a patches mais complicados em vez de mudanças profundas na estrutura da base
de código. Se uma base de código de código aberto requer operações mentais difíceis, as pessoas podem ser
menos propensas a se tornarem mantenedoras. Portanto, é importante ter uma noção do desempenho de sua
base de código nas diferentes dimensões.
A lista de dimensões cognitivas pode ser usada como uma espécie de checklist para uma base de código.
Nem todas as dimensões são importantes para todas as bases de código, mas investigar regularmente cada
uma delas e decidir como sua base de código está se saindo ajudará você a manter a usabilidade. Idealmente,
você analisa as dimensões de uma base de código regularmente (por exemplo, uma vez por ano).

EXERCÍCIO 12.3 Preencha a tabela a seguir para entender as dimensões em jogo. Quais
dimensões são importantes para sua base de código? Qual deles pode ser melhorado?
Machine Translated by Google

Examinando as propriedades das bases de código 201

Dimensão Relevante? Poderia ser melhorado?

Propensão a erros

Consistência

Visibilidade

Dependências ocultas

Provisório

Viscosidade

Avaliação progressiva

Expressividade do papel

Proximidade do mapeamento

Operação mental difícil

Notação secundária

Abstração

Visibilidade

12.1.3 Manobras de projeto e seus trade-offs


Fazer alterações em uma base de código para melhorar uma determinada dimensão em uma base de código é chamado
uma manobra de projeto. Por exemplo, adicionar tipos a uma base de código é uma manobra de design que
melhora a propensão a erros e altera os nomes das funções para estar mais alinhado com o
domínio do código é uma manobra de design que melhora a proximidade do mapeamento.

EXERCÍCIO 12.4 Examine a lista que você criou no exercício 12.3 para as dimensões que
podem ser melhoradas. Você vê manobras de design que você poderia aplicar?
Qual seria o efeito dessas manobras em outras dimensões?

Impacta as dimensões Impacta as dimensões


Dimensão Manobra de projeto
positivamente? negativamente?

Muitas vezes, uma manobra de projeto (ou seja, uma mudança em uma dimensão) causa mudanças
Outra dimensão. Como as dimensões interagem com precisão pode depender muito
sua base de código, mas existem algumas dimensões que geralmente estão em desacordo umas com as outras.
Machine Translated by Google

202 CAPÍTULO 12 Projetando e aprimorando sistemas maiores

PROPENSÕES A ERROS VS. VISCOSIDADE

Se você quiser evitar que o usuário de sua biblioteca ou estrutura cometa erros, geralmente fará isso
fazendo com que o usuário insira informações adicionais. O exemplo mais conhecido de uma dimensão
que diminui a propensão a erros é permitir que um usuário adicione tipos a entidades. Se o compilador
souber o tipo de uma entidade, essa informação pode ser usada para evitar erros, como adicionar
acidentalmente uma lista a uma string.
No entanto, quando tudo em um sistema é digitado, isso pode representar um trabalho extra para
o usuário. Por exemplo, você pode precisar converter variáveis em um tipo diferente para poder usá-las
da maneira desejada. Quando as pessoas não gostam de sistemas de tipos, mesmo tendo em vista
seu benefício na prevenção de tipos, isso geralmente se deve à viscosidade extra que um sistema de
tipos adiciona.
PROVISIONALIDADE E AVALIAÇÃO PROGRESSIVA VS. PROPENSÕES A ERROS

Um sistema com muita provisoriedade e avaliação progressiva permite que o usuário esboce e execute
código incompleto ou imperfeito. Embora essas dimensões possam ajudar alguém a pensar sobre o
problema em questão, programas incompletos podem não ser excluídos e programas imperfeitos
podem nunca ser aprimorados, levando a um código difícil de entender e, portanto, difícil de depurar,
impactando a propensão a erros.
PAPEL EXPRESSIVIDADE VS. DIFUSÃO

Vimos que a expressividade do papel pode ser criada adicionando elementos sintáticos adicionais,
como parâmetros de nomes. No entanto, os rótulos extras fazem com que o código seja mais longo. O
mesmo vale para anotações de tipo, que também expressam os papéis que as variáveis desempenham,
mas aumentam o tamanho de uma base de código.

12.2 Dimensões e atividades


No capítulo anterior, discutimos cinco atividades de programação diferentes: busca, compreensão,
transcrição, incrementação e exploração. Cada atividade coloca diferentes restrições nas dimensões
cognitivas que uma base de código precisa otimizar.
A relação entre dimensões e atividades é apresentada na tabela 12.1.

12.2.1 Impacto das dimensões em diferentes atividades


No capítulo 11, descrevemos cinco atividades diferentes que as pessoas realizam quando programam.
Na verdade, essas atividades também derivam da versão original da estrutura CDN. Black bem, Petre
e Green descreveram essas atividades porque as diferentes atividades interagem com as dimensões.
Algumas atividades exigem que uma determinada dimensão seja alta, enquanto outras funcionam
melhor se uma dimensão for baixa, conforme mostrado na tabela 12.1.

PESQUISA Ao

pesquisar, algumas dimensões desempenham um papel importante. Por exemplo, dependências ocultas podem prejudicar a atividade de

pesquisa, pois se você não souber qual código é chamado de onde, pode ser difícil decidir o que ler em seguida e, assim, desacelerar a
pesquisa. A difusão faz com que o código seja mais longo, o que também prejudica a pesquisa simplesmente porque há mais código para

pesquisar.
Machine Translated by Google

Dimensões e atividades 203

Por outro lado, a notação secundária pode ajudar na busca porque comentários e
nomes de variáveis podem indicar onde as informações podem ser encontradas.

COMPREENSÃO

Algumas dimensões são especialmente importantes ao compreender o código. Por exemplo,


baixa visibilidade em uma base de código pode prejudicar a compreensão porque dificulta a visualização,
e assim entender, como classes e funções se relacionam.
A expressividade do papel, por outro lado, pode ajudar na compreensão. Se o tipo e
papel das variáveis e outras entidades é claro, a compreensão pode ser mais fácil.
TRANSCRIÇÃO

Ao transcrever (ou seja, implementar um recurso com base em um plano predefinido) alguns
dimensões que de outra forma são boas podem ser prejudiciais – por exemplo, consistência. Enquanto
uma base de código consistente pode ser mais fácil de compreender, você terá que fazer o novo
o código se encaixa na base de código ao implementar um novo recurso, o que pode levar a
esforço mental. Claro, esse esforço pode valer a pena a longo prazo, mas ainda é
esforço que precisa ser gasto.
INCREMENTAÇÃO

A adição de novos recursos a uma base de código é suportada principalmente pela proximidade do mapeamento para o
domínio. Se a base de código permite pensar no objetivo do código, em vez de
conceitos de programação, será mais fácil adicionar novo código. Bases de código com alta viscosidade,
por outro lado, dificultam a adição de código.
EXPLORAÇÃO

Explorar novas ideias de design enquanto na base de código (ou seja, explorar) é mais suportado
por sistemas que têm boa provisoriedade e avaliação progressiva.
Operações mentais difíceis e abstrações podem prejudicar a exploração porque colocam
uma alta carga cognitiva no programador, limitando a carga que pode ser gasta em
explorando o problema e o espaço da solução.

Tabela 12.1 Visão geral das dimensões e as atividades que elas apoiam ou prejudicam

Dimensão Ajuda Prejudica

Propensão a erros Incrementação

Consistência Pesquisa, compreensão Transcrição

Difusão Procurando

Dependências ocultas Procurando

Provisório Exploração

Viscosidade Transcrição, incrementação

Avaliação progressiva Exploração

Expressividade do papel Compreensão


Machine Translated by Google

204 CAPÍTULO 12 Projetando e aprimorando sistemas maiores

Tabela 12.1 Visão geral das dimensões e as atividades que elas apoiam ou prejudicam (continuação)

Dimensão Ajuda Prejudica

Proximidade do mapeamento Incrementação

Operações mentais difíceis Transcrição, incrementação, exploração

Notação secundária Procurando

Abstração Compreensão Exploração

Visibilidade Compreensão

12.2.2 Otimizando sua base de código para atividades esperadas


Vimos que diferentes atividades colocam diferentes restrições em um sistema. Portanto, você deve entender
as ações mais prováveis que as pessoas realizarão em sua base de código. Bibliotecas relativamente antigas
e estáveis são mais propensas a serem pesquisadas do que bibliotecas
incrementados, enquanto novos aplicativos são mais propensos a serem incrementados e transcritos.
Isso significa que, durante a vida útil de uma base de código, podem ser necessárias manobras de projeto
para tornar a base de código mais alinhada com as atividades mais prováveis.

EXERCÍCIO 12.5 Pense em sua base de código. Quais atividades são mais prováveis
ocorrer? As atividades se estabilizaram nos últimos meses? Quais dimensões desempenham um
papel nessas atividades e como sua base de código funciona
essas dimensões?

Resumo
ÿ CDN é uma estrutura que ajuda os programadores a prever o efeito cognitivo
linguagens gramaticais terão em seus usuários.
ÿ CDCB é uma extensão do CDN que ajuda os programadores a entender o impacto
suas bases de código, bibliotecas e frameworks terão em seus usuários. ÿ Em muitos

casos, compensações entre diferentes dimensões devem ser feitas. Improviso


gem de uma dimensão pode diminuir outra dimensão.
ÿ Melhorar o design das bases de código existentes de acordo com a dimensão cognitiva do
quadro de notações pode ser feito com uma manobra de design.
ÿ Diferentes atividades colocam demandas diferentes nas dimensões de uma base de código
otimiza para.
Machine Translated by Google

Como integrar novos


desenvolvedores

Este capítulo aborda ÿ

Comparando as maneiras como especialistas e iniciantes pensam


ÿ Melhorando a integração de novos desenvolvedores em uma
base de código

ÿ Apoiar novos desenvolvedores enquanto aprende a usar uma


nova linguagem de programação ou estrutura

Até agora, examinamos como ler e organizar o código. Como um desenvolvedor mais sênior, no entanto, você
provavelmente lutará com sua própria confusão e com a confusão de outras pessoas mais juniores com quem
trabalha. Em muitos casos, você vai querer gerenciar a carga cognitiva que os juniores estão experimentando para
garantir que eles aprendam de forma mais eficaz.

Neste capítulo, examinaremos como melhorar seu processo de integração, seja para integrar um desenvolvedor
experiente em uma base de código desconhecida ou um programador iniciante.

Para fazer isso, primeiro examine como especialistas e iniciantes pensam e se comportam de maneira diferente.
Em seguida, abordaremos uma variedade de atividades que uma equipe pode realizar para integrar uma nova equipe

205
Machine Translated by Google

206 CAPÍTULO 13 Como integrar novos desenvolvedores

membros. Ao final deste capítulo, você estará familiarizado com três técnicas e
atividades para apoiar os recém-chegados de forma mais eficaz.

13.1 Problemas no processo de integração


Como desenvolvedor sênior, você provavelmente já encontrou vários cenários nos quais
você tinha que integrar os recém-chegados. Este pode ser um recém-chegado a uma equipe ou a um
projeto fonte. Muitos programadores não são necessariamente treinados para ensinar ou orientar, tornando o
processo de integração frustrante para ambos os lados. Neste capítulo, vamos
mergulhar no que acontece no cérebro dos recém-chegados durante o processo de integração
e como você pode gerenciar melhor esse processo.
Os processos de integração que testemunhei funcionaram mais ou menos da seguinte forma:

ÿ Um desenvolvedor sênior lança muitas informações novas para um recém-chegado. Quantidade


de informação é demais para processar, causando alta carga cognitiva. Por exemplo, o integrador
apresenta novas pessoas, o domínio da base de código, o fluxo de trabalho e a base de código de uma
só vez.

ÿ Após a introdução, o desenvolvedor sênior faz uma pergunta ao recém-chegado ou


dá ao recém-chegado uma tarefa. O desenvolvedor sênior geralmente vê isso como algo
extremamente simples, por exemplo, corrigir um pequeno bug ou adicionar um pequeno recurso.
ÿ O recém-chegado falha por causa da alta carga cognitiva, causada por uma combinação de
falta de pedaços relevantes para o domínio e/ou linguagem de programação e falta de
habilidades automatizadas relevantes.

Qual é o problema com as interações deste desenvolvedor sênior com um recém-chegado? O


O problema mais profundo neste cenário é que o desenvolvedor sênior está sobrecarregando a capacidade da
memória de trabalho do recém-chegado, pedindo-lhe para aprender muito no
mesmo tempo. Vamos refrescar nossa memória com alguns dos principais conceitos abordados anteriormente
capítulos. No capítulo 3, abordamos a carga cognitiva, que é o esforço do cérebro em um determinado
problema. Vimos que quando o cérebro está experimentando muita carga cognitiva, o pensamento eficaz é
inibido. No capítulo 10, vimos que quando você experimenta muito
carga cognitiva intrínseca e alheia, você não tem espaço para a carga pertinente, ou seja,
você não será capaz de se lembrar de novas informações.
Como a memória de trabalho do recém-chegado está sobrecarregada, ele não pode programar com eficiência
na nova base de código, nem pode reter as novas informações adequadamente. Mais que
uma vez que vi isso levar à frustração e a suposições erradas de ambos os lados. O time
O lead pode presumir que o recém-chegado não é muito brilhante, e o recém-chegado supõe que o projeto será
muito difícil. Esse não é um bom terreno para começar uma colaboração adicional.
Uma das razões pelas quais as pessoas mais velhas muitas vezes lutam para ensinar e
explicar é a “maldição da perícia”. Uma vez que você tenha dominado uma certa habilidade o suficiente,
você inevitavelmente esquecerá como foi difícil aprender essa habilidade ou conhecimento. Você irá, portanto,
superestimar quantas coisas novas um recém-chegado pode processar ao mesmo tempo.
Tenho certeza de que em algum lugar nos últimos meses você disse que algo “não estava
tão difícil”, “na verdade muito fácil” ou “trivial”. Eu diria que em muitos desses casos
você estava falando de um conhecimento que levou algum tempo para você adquirir. Momentos
Machine Translated by Google

Diferenças entre especialistas e novatos 207

onde você diz: "Uau, isso é fácil!" podem ser momentos em que você cai na maldição de
perícia. A primeira coisa que você pode fazer para tornar o processo de integração mais fácil é perceber que
provavelmente não é tão fácil para a pessoa que está aprendendo.

13.2 Diferenças entre especialistas e novatos


Muitas vezes os especialistas pensam que os novatos podem raciocinar da mesma forma que eles, mas talvez mais lento ou
com uma imagem incompleta de toda a base de código. A lição mais importante desta
capítulo é entender que especialistas e novatos pensam e se comportam de maneiras muito diferentes.
No início do livro, abordamos os motivos pelos quais os especialistas podem pensar de forma diferente. Primeiro, um
o cérebro do especialista armazena uma grande coleção de memórias relacionadas que sua memória de trabalho pode
buscar no LTM. Essas memórias incluem estratégias que elas deliberadamente
aprenderam, como escrever um teste para a questão primeiro, ou memórias episódicas de coisas que eles
tentado no passado, como reinicializar o servidor. Os especialistas não têm necessariamente tudo
as respostas. Eles também podem precisar pesar opções diferentes, mas geralmente eles
já sabe coisas sobre problemas e tem alguma idéia de como abordá-los.
Em segundo lugar, um especialista pode fragmentar código de forma muito eficaz e todos os tipos de código adjacente
artefatos como mensagens de erro, testes, problemas e soluções. Um especialista provavelmente
dê uma olhada em uma parte do código e reconheça que está, por exemplo, esvaziando uma fila.
Um iniciante pode precisar ler o código linha por linha. Uma mensagem de erro simples como
“Índice de matriz fora dos limites” para um especialista representa um conceito. Para um programador iniciante, isso
pode representar três elementos separados, levando a mais
carga. Muitas situações em que as pessoas assumem que um novo colega “não é um programador tão forte” são, na
verdade, situações de maldição em que o novato está sobrecarregado.

13.2.1 Comportamento dos iniciantes com mais profundidade

Para entender melhor o comportamento dos programadores iniciantes, vamos considerar o útil
arcabouço psicológico do neopiagetismo, que explica o comportamento das pessoas quando confrontadas com novas
informações. O Neo-Piagetismo baseia-se no trabalho de Jean Piaget, um influente psicólogo do desenvolvimento que
se concentrou nos quatro estágios de desenvolvimento para
crianças pequenas. Para nossos propósitos, o neo-piagetismo descreve como os programadores se comportam
quando eles estão apenas começando a conhecer uma linguagem de programação, base de código ou paradigma.

MODELO ORIGINAL DA PIAGET

Antes que eu possa ensinar como os programadores se comportam em cenários de aprendizado desconfortáveis, vamos
repassar os estágios comportamentais que começaram na infância. primeiro tenho que explicar
O modelo original de Piaget para crianças pequenas, mostrado na tabela 13.1. No primeiro nível,
que descreve o comportamento de crianças de 0 a 2 anos de idade, as crianças não podem
fazer planos ou supervisionar situações. Eles simplesmente experimentam as coisas (sentido) e agem
(motor) sem muita estratégia envolvida. No segundo nível, quando as crianças
entre 2 e 7 anos de idade, as crianças começam a formular hipóteses, mas essas hipóteses
muitas vezes não são muito fortes. Por exemplo, uma criança de 4 anos pode levantar a hipótese de que está chovendo
porque as nuvens são tristes. Isso não é exatamente correto, mas você pode ver que eles estão tentando
encontrar explicações para suas observações.
Machine Translated by Google

208 CAPÍTULO 13 Como integrar novos desenvolvedores

Na terceira fase, dos 7 aos 11 anos, as crianças começam a formular hipóteses sobre as quais
podem raciocinar, mas apenas em situações concretas. Eles podem, por exemplo, decidir sobre uma boa
se movem em um jogo de tabuleiro, mas acham difícil generalizar seus pensamentos e razões sobre
se esse movimento será sempre bom para tabuleiros diferentes. Esse tipo de formalidade
raciocínio acontece no estágio final – o estágio operacional concreto – quando as crianças
têm mais de 11 anos.

Tabela 13.1 Visão geral dos estágios de desenvolvimento cognitivo de Piaget

Estágio Características Descreve crianças com idade

Estágio sensório-motor As crianças carecem de um plano ou estratégia; eles 0-2 anos


simplesmente sentem e agarram as coisas.

Estágio pré-operacional As crianças começam a formular hipóteses e planos, mas não 2-7 anos
os utilizam de forma confiável para pensar.

Estágio operacional concreto As crianças podem raciocinar sobre coisas concretas que 7-11 anos
podem ver, mas acham difícil tirar conclusões gerais.

Estágio operacional formal As crianças podem se envolver em raciocínio formal. 11 anos e mais velhos

MODELO NEO-PIAGETIANO PARA PROGRAMAÇÃO

O modelo de Piaget recebeu algumas críticas, principalmente porque ele usou seus próprios filhos para
criar o modelo. No entanto, seu trabalho lançou as bases para o neo-piagetismo, que tem grande
valor na compreensão do pensamento de programadores iniciantes. A ideia central de
neo-Piagetismo é que os níveis de Piaget não são gerais, mas específicos de domínio. Pessoas
pode operar no estágio operacional formal em um domínio, como programação em Java,
enquanto eles ainda se comportam no nível sensório-motor para programação em Python. Pode
mesmo que alguém opere no estágio operacional formal por um certo
base de código, mas volta para um nível inferior em uma nova base de código. A Tabela 13.2 descreve o
modelo neopiagetiano e suas implicações para a programação, conforme descrito por Australian
professor Raymond Lister.1

Tabela 13.2 Visão geral dos estágios neo-piagetianos de desenvolvimento e comportamento de programação correspondente

Estágio Características Comportamento de programação

Sensorimotor As crianças carecem de um plano ou O programador tem uma compreensão incoerente da


estágio estratégia; eles simplesmente sentem e execução do programa. Neste estágio, um programador não
agarram as coisas. pode rastrear corretamente um programa.

Estágio pré- As crianças começam a formular hipóteses e O programador pode prever manualmente de forma confiável o
operacional planos, mas não os utilizam de forma confiável. resultado de várias linhas de código, por exemplo, criando uma
tabela de rastreamento. Um programador pré-operacional muitas
vezes faz suposições sobre o que um pedaço de código faz.

1
Raymond Lister, “Rumo a uma epistemologia de desenvolvimento da programação de computadores”, https://dl.acm.org/
doi/10.1145/2978249.2978251.
Machine Translated by Google

Diferenças entre especialistas e novatos 209

Tabela 13.2 Visão geral dos estágios neo-piagetianos de desenvolvimento e comportamento de programação correspondente

Estágio Características Comportamento de programação

Concreto As crianças podem raciocinar sobre contra O programador raciocina sobre o código dedutivamente
estágio operacional criam coisas que podem ver, mas acham difícil lendo o próprio código em vez de usar a abordagem indutiva
tirar conclusões gerais. pré-operacional.

Formal As crianças podem se envolver em O programador pode agora raciocinar de forma lógica,
estágio operacional raciocínio formal. consistente e sistemática.

O raciocínio operacional formal inclui refletir sobre suas


próprias ações, o que é essencial para a depuração.

No primeiro nível, ilustrado pelo programador mais à esquerda na figura 13.1, os programadores
não podem rastrear corretamente um programa (ou seja, eles não podem criar uma tabela de rastreamento como
descrito no capítulo 4). O comportamento nesta fase é comum para pessoas que não têm ou
pouca experiência em programação, mas também pode ocorrer quando os programadores alternam entre
linguagens muito diferentes (por exemplo, de JavaScript a Haskell). Como a execução do programa é tão
diferente nessas duas linguagens, um programador experiente em JavaScript
pode ter dificuldades em rastrear um programa Haskell. Porque eles estão tão focados no
código, que para eles ainda não é fácil de compreender, explicando princípios gerais
separado do código não é uma maneira útil de ensinar. Por exemplo, um programador sensório-motor que está
percorrendo o código do banco de dados não será ajudado se você começar a explicar
como o banco de dados está configurado em outro lugar no código. Primeiro, eles precisam entender o modelo
de execução.
O segundo estágio é o estágio pré-operacional, no qual um programador pode rastrear
pequenos pedaços de código, mas essa também é a única maneira de raciocinar sobre o código: usando
suas habilidades de rastreamento recém-adquiridas. Programadores pré-operacionais acham difícil explicar
o significado desse mesmo código. O programador pré-operacional é muito focado em
o próprio código e acha difícil olhar para outros artefatos, principalmente diagramas.
Apoiar programadores pré-operacionais em sua leitura ou escrita de código dando
os diagramas não serão úteis. Porque esses programadores raciocinam indutivamente
sobre o código, eles geralmente adivinham o comportamento do código com base em alguns traços.
A segunda etapa, eu acho, é mais frustrante para os programadores, mas também para os
pessoas integrando-as ou ensinando-as. Como é difícil para os programadores no estágio pré-operacional
entender o significado mais profundo do código, eles geralmente estão adivinhando.
Isso pode fazer com que os programadores neste nível pareçam erráticos. Às vezes, seus palpites são
no local, com base no conhecimento prévio que transfere (ou sorte), mas cinco minutos depois eles
expressar ideias totalmente irracionais. Esta pode ser a situação em que a pessoa que está sendo
onboarded fica frustrado e acha que o programador júnior não é inteligente ou não está dando o seu melhor. No
entanto, o estágio pré-operatório é um estágio necessário para avançar
para a próxima etapa. Treinar recém-chegados expandindo seu vocabulário de código com cartões flash pode
ajudá-los a avançar.
Machine Translated by Google

210 CAPÍTULO 13 Como integrar novos desenvolvedores

xyz xyz xyz 12 5 xyz 12 5


12 5
5 5 5
12 12 12
5 5 5

x = 12
y=5
Eu não sei o z se torna y. Os valores
z= Eu não sei o
yy = x que isso que isso y se torna x. de xey são
x=z faz. x se torna z. trocados.
faz.

Sensorimotor Pré-operacional Operacional concreto Operacional formal

Figura 13.1 Visão geral dos quatro diferentes níveis neo-piagetianos para programação

No terceiro estágio, os programadores operacionais concretos podem raciocinar sobre o código sem
rastreá-lo meticulosamente. Eles fazem isso usando conhecimento prévio: reconhecendo partes
familiares no código, lendo comentários e nomes e rastreando código apenas quando necessário (por
exemplo, ao depurar). Lister observa em seu estudo que é útil para os programadores usarem
diagramas para apoiar seu pensamento apenas no estágio operacional concreto. Os programadores
operacionais concretos começam a se comportar como programadores apropriados: eles podem
raciocinar sobre o código e podem fazer um plano e executá-lo ao escrever o código. No entanto, às
vezes eles ainda podem não ter o entendimento global de uma base de código e também podem lutar
com a reflexão sobre se algo é uma boa estratégia a seguir. Isso pode se mostrar em um
comprometimento excessivo com uma primeira estratégia (por exemplo, um programador júnior que
tentou por um dia inteiro corrigir um determinado bug e continua falhando e tentando novamente, em
vez de recuar e refletir se a estratégia escolhida é a correta 1).

A etapa final é a operação formal. Este é um programador experiente que pode raciocinar sobre
código e sobre seu próprio comportamento confortavelmente e, portanto, não é tão interessante para o
processo de integração. Esses programadores provavelmente ficarão à vontade para aprender os
detalhes das bases de código e poderão pedir ajuda quando necessário.

APRENDER NOVAS INFORMAÇÕES PODE FAZER VOCÊ ESQUECER TEMPORARIAMENTE AS COISAS

Os quatro estágios são apresentados como estágios distintos, mas na realidade não são. Ao aprender
um novo conceito de programação ou um novo aspecto de uma base de código, os alunos podem
voltar temporariamente para um nível inferior. Se alguém puder ler de forma confiável as funções do Python
Machine Translated by Google

Diferenças entre especialistas e novatos 211

sem rastreamento, mas é então exposto a funções variádicas usando *args, eles podem
precisam rastrear algumas chamadas de função para ver o que acontece antes que possam confortavelmente
leia-os sem rastrear novamente.

EXERCÍCIO 13.1 Os quatro comportamentos diferentes ocorrem comumente na aprendizagem


processos dentro das empresas. Reflita sobre os níveis neopiagetianos
com um exemplo que você viu acontecer na prática. Preencha o seguinte
tabela.

Estágio Comportamento Exemplo

Sensorimotor Neste estágio, um programador não pode rastrear


estágio corretamente um programa.

Estágio pré- O programador pode prever manualmente de forma confiável


operacional o resultado de várias linhas de código, por exemplo, fazendo
uma tabela de rastreamento. Um programador pré-operacional
geralmente faz suposições sobre o que um pedaço de código
faz.

Estágio O programador raciocina sobre o código dedutivamente


operacional concreto lendo o próprio código em vez de usar a abordagem
indutiva pré-operacional.

Estágio O programador pode agora raciocinar de forma lógica,


operacional formal consistente e sistemática.

O raciocínio operacional formal inclui refletir sobre suas


próprias ações, o que é essencial para a depuração.

13.2.2 Diferença entre ver conceitos de forma concreta e abstrata


Vimos que programadores iniciantes e experientes agem e pensam de forma diferente.
A pesquisa também mostra que os especialistas costumam falar sobre conceitos de maneiras diferentes, de maneira muito
termos genéricos e abstratos. Por exemplo, ao explicar uma função variádica em
Python para alguém novo no conceito, especialistas podem dizer que é uma função que pode
aceitar um número variável de argumentos. No entanto, eles podem deixar muitas perguntas sem resposta,
por exemplo, como acessar todos os diferentes argumentos, dar nomes a cada
argumento, ou se há um limite no número de argumentos.
No entanto, os novatos em uma linguagem ou base de código se beneficiam de ambas as formas de
explicação. Idealmente, a compreensão de um iniciante segue uma onda semântica, um conceito definido por
Cientista australiano Karl Maton, conforme ilustrado pela figura 13.2.2
Seguindo a onda semântica, os primeiros iniciantes precisam entender o conceito genérico: para que
serve e por que você precisa conhecê-lo. Por exemplo, uma função variádica
é útil porque permite que você use quantos argumentos em uma função forem necessários.

2 Karl Maton, “Fazendo Ondas Semânticas: Uma Chave para a Construção Cumulativa do Conhecimento”, Linguística e Educação, vol.
26, não. 1, pp. 8–22, 2013, https://www.sciencedirect.com/science/article/pii/S0898589812000678.
Machine Translated by Google

212 CAPÍTULO 13 Como integrar novos desenvolvedores

Depois que os iniciantes viram o que o conceito faz em geral, eles seguem a curva
down, um processo conhecido como unpacking. O iniciante está então pronto para aprender detalhes sobre
o conceito. Por exemplo, eles agora podem aprender que um * é usado para indicar uma variável
função em Python e que Python implementa a lista de argumentos como uma lista, então em
na realidade não há argumentos múltiplos; há um argumento que pode conter todos
argumentos da função como elementos.
Finalmente, o iniciante precisa voltar ao nível abstrato, afastando-se
de detalhes e se sentir confortável sabendo como o conceito funciona em geral. Isto
fase é chamada de reembalagem. Quando um conceito é devidamente reembalado, o aluno pode pensar
sobre isso sem focar em detalhes concretos. A reembalagem também envolve a integração do
conhecimento no LTM em relação ao conhecimento prévio – por exemplo, “C++ suporta
funções variádicas, mas Erlang não.”

Compreensão abstrata
Funções Variádicas em diferentes contextos
tem vários argumentos. e idiomas

Resumo

Nível
de Usar *
Desembalagem Reembalagem
abstração
Args são listas.

Concreto

Tempo

Figura 13.2 A onda semântica representando a explicação ideal que começa abstratamente,
permite que o aluno descompacte o conhecimento passando por detalhes concretos e
permite que o aluno reembale o conhecimento aprendido em seu LTM.

Para iniciantes, existem três antipadrões distintos que eles usam em sua profundidade e uma variedade
de explicações, cada uma ilustrada pela figura 13.3. O primeiro antipadrão é chamado de
high flatline e significa que você só usa termos abstratos. Um recém-chegado ao Python pode aprender
que o Python tem funções variáveis e por que elas são úteis, mas se eles nunca virem o
sintaxe, há muito o que aprender mais tarde.
O segundo antipadrão é o inverso, uma linha plana baixa. Alguns especialistas sobrecarregam os
iniciantes com detalhes sem explicar por que o conceito é relevante e útil. Iniciando
com “Você pode fazer uma função variadic com um * e então o Python vê todos os argumentos como
uma lista” não vai significar muito quando o iniciante não sabe quando usar um
função variada.
Machine Translated by Google

Atividades para um melhor processo de onboarding 213

Resumo
Linha plana alta Escadas rolantes para baixo

Linha plana baixa


Concreto

Figura 13.3 Três antipadrões diferentes: flatlining alto (apenas explicações abstratas), flatlining baixo
(apenas explicações concretas) e escadas rolantes descendentes (começando de cima para baixo, mas
esquecendo de deixar espaço para reembalagem)

O antipadrão final começa no abstrato e depois desce para o concreto, seguindo


a onda semântica. Mas depois dos detalhes concretos, nesse antipadrão o especialista esquece
para permitir que os recém-chegados reembalem o significado. Em outras palavras, o especialista mostra aos iniciantes
o porquê e depois o como do conceito, mas não lhes dá tempo para integrar
o novo conhecimento em seu LTM. Você pode apoiar este reempacotamento perguntando explicitamente
para as semelhanças que as pessoas vêem entre o novo conceito e as informações preliminares.

EXERCÍCIO 13.2 Escolha um conceito que você conheça bem e encontre explicações que se encaixem
todos os três locais na onda semântica, como mostrado na figura 13.1.

13.3 Atividades para um melhor processo de integração


No restante do capítulo, veremos como melhorar o processo de integração. A primeira e mais importante coisa
que você pode fazer é gerenciar deliberadamente a carga cognitiva das pessoas que você está integrando.
Obviamente, também é muito útil se o
o recém-chegado também pode gerenciar sua própria carga cognitiva. Apresentando conceitos como
tipos de memória (por exemplo, memória de longo prazo, curto prazo e de trabalho), carga cognitiva e
pedaços podem facilitar a comunicação de uma equipe. É muito mais fácil se um recém-chegado pode dizer: “Eu
experimentar muita carga ao ler este código” ou “Acho que faltam pedaços para
Python” em vez de “Estou confuso”. Vamos agora mergulhar em três atividades de integração com mais
profundidade.

13.3.1 Limitar tarefas a uma atividade de programação


No capítulo 11, descrevemos cinco atividades que as pessoas podem fazer em uma base de código: transcrição,
exploração, compreensão, busca e incremento. Uma das questões
com o onboarding é que os novatos são solicitados a realizar pelo menos quatro atividades diferentes: buscar o
lugar certo para implementar o recurso ou informações relevantes, compreender o novo código-fonte, explorar a
base de código para obter uma melhor
entender e incrementar a base de código com um novo recurso.
Como também vimos no capítulo 11, as diferentes atividades colocam diferentes
requisitos do programador e do sistema. Alternar entre as diferentes atividades é pedir muito para um recém-
chegado. Mesmo sabendo da programação
Machine Translated by Google

214 CAPÍTULO 13 Como integrar novos desenvolvedores

idioma e talvez até mesmo o domínio, realizar muitas tarefas diferentes é desnecessariamente difícil.

Durante o processo de integração, é melhor escolher especificamente as atividades em cada um dos


as cinco categorias e faça com que os recém-chegados as façam uma a uma. Vamos explorar as cinco
atividades com mais detalhes e estudar exemplos de como cada atividade pode ajudar os recém-chegados.

Tabela 13.3 Visão geral das atividades de programação e como elas podem ser usadas para apoiar os recém-chegados em um
projeto

Atividade Exemplos usados para dar suporte aos embarcados

Exploração Navegando na base de código para obter uma noção geral da base de código

Procurando Encontrar uma classe que implementa uma determinada interface

Transcrição Dar ao recém-chegado um plano claro para implementar um determinado método que deve ser implementado

Compreensão Compreender os aspectos do código, por exemplo, resumir um método específico em


linguagem natural

Incrementação Adicionando um recurso a uma classe existente, incluindo a criação do plano para o recurso

As diferentes atividades podem construir umas sobre as outras e trabalhar em partes relacionadas do código.
Por exemplo, uma primeira tarefa pode ser procurar uma aula, seguida de transcrever um
dentro dessa classe em código e, em seguida, incrementando a classe de uma maneira mais complexa.
Você também pode alternar tarefas em que o foco é aprender novos conceitos de programação com tarefas
que focam mais em conhecer o domínio, dependendo
no conhecimento prévio existente do embarcado.

EXERCÍCIO 13.3 Pense em uma atividade concreta que um recém-chegado poderia fazer em todos os cinco
categorias quando novo em sua base de código.

Pode-se também imaginar que uma equipe pode criar e manter documentos para
ajudar ainda mais os recém-chegados (por exemplo, apoiar a exploração com bons comentários e
documentos de arquitetura explicando módulos, subsistemas, estruturas de dados e
algoritmos usados no sistema).

13.3.2 Suporta a memória do embarcado


Como explicamos, a primeira coisa que importa na integração de recém-chegados é entender que as coisas
não são fáceis. Como abordamos no capítulo 2, os recém-chegados veem e lembram
coisas diferentes dos especialistas. Pode não precisar ser dito, mas empatia e paciência são
importante!
Além de ter um vocabulário compartilhado de conceitos da ciência cognitiva, existem
três fases nas quais você pode melhorar o processo, que se relacionam com as três formas de
confusão que estudamos no capítulo 1.
Machine Translated by Google

Atividades para um melhor processo de onboarding 215

APOIE O LTM: EXPLIQUE AS INFORMAÇÕES RELEVANTES

Primeiro, você pode preparar o processo de integração para recém-chegados entendendo profundamente as
informações relevantes que desempenham um papel ao trabalhar com a base de código. Isto
pode ser feito com a equipe de saída, antes mesmo da chegada do recém-chegado.
Por exemplo, você pode querer documentar todos os conceitos de domínio importantes que as pessoas
provavelmente encontrarão no código. Outra informação relevante é tudo
bibliotecas, frameworks, bancos de dados e outras ferramentas externas usadas no código. Uma frase
como, "Nós usamos Laravel para este aplicativo da web, que implantamos no Heroku com Jenkins" pode
não exige nenhum esforço de um desenvolvedor existente, mas se você não sabe o que mesmo um desses
tools é, você pode perder todo o significado da frase. E, claro, o recém-chegado
podem saber o que é um framework web ou um servidor de automação em abstrato, mas se eles
não conheço esses nomes específicos, é difícil entender o significado e lembrar.

DOMÍNIO SEPARADO APRENDENDO COM A EXPLORAÇÃO DO CÓDIGO Examinando todos os


conceitos separadamente da introdução do código tornará muito mais fácil
saiba mais sobre o código. Isso parece uma coisa pequena, mas pode fazer uma grande
diferença. Você pode até fazer um baralho de flashcards com domínio relevante
e conceitos de programação para o recém-chegado praticar.

Como nota lateral, mesmo separado do processo de onboarding, ter uma lista atualizada de todos os
conceitos de domínio e programação relevantes para um projeto também podem ajudar
desenvolvedores.

EXERCÍCIO 13.4 Escolha um projeto no qual você trabalha com frequência. Crie duas listas que
poderia ajudar os recém-chegados: um contendo conceitos de domínio importantes e seus
descrição e um com todas as bibliotecas, frameworks e conceitos de programação importantes
que a base de código usa. Preencha a tabela a seguir.

Conceitos de domínio Conceitos/bibliotecas de programação

Conceito Definição

Conceito/módulo/biblioteca Uso/definição
Machine Translated by Google

216 CAPÍTULO 13 Como integrar novos desenvolvedores

APOIE O STM: PREPARE TAREFAS PEQUENAS E FOCADAS


Outra coisa que vejo acontecer nos processos de onboarding, que não correm perfeitamente, é
que o recém-chegado explica o código. O líder da equipe clica no código, mostrando
partes relevantes do código. Após a sessão de explicação, o recém-chegado é convidado a
comece com um recurso relativamente simples para “conhecer” a base de código. O mesmo pode
acontecer em projetos de código aberto onde solicitações de recursos simples são rotuladas como
amigáveis para iniciantes. Embora isso pareça muito acolhedor, pode apresentar um problema cognitivo.
O recém-chegado será solicitado a fazer várias atividades de programação e, portanto, o
o cérebro do recém-chegado agora está fazendo várias coisas: conhecer o código, pesquisar
através dele, e também implementando um recurso. Isso provavelmente sobrecarregará o recém-chegado
STM porque eles não podem (ainda) navegar facilmente na base de código. Eles passaram muito tempo
procurar código e ler código distrai da tarefa em mãos. É melhor
dividir os esquemas em várias fases.

ENTENDER É UMA TAREFA MELHOR BEM-VINDO DO QUE CONSTRUIR Se você quer


que um recém-chegado entenda uma certa parte do código, peça que ele entenda um
peça em vez de dar-lhes uma tarefa de implementação. Por exemplo, pergunte
que escrevam um resumo de uma classe existente ou escrevam todas as classes envolvidas
na execução de um determinado recurso.

Dar aos recém-chegados uma tarefa focada será menos pesado no STM e, portanto, mais provável de
deixe espaço para carga relevante para lembrar coisas importantes sobre o código. Além de ser mais
fácil e eficaz para o iniciante, ter bons resumos de código
será útil como documentação para outros recém-chegados, mais do que mais recursos.
Se você optar por fazer com que um recém-chegado implemente um pequeno recurso, ou seja, de
claro, possível também. Mas nesse código, é melhor remover outros aspectos que criam
carga cognitiva, como pesquisar por meio de código. Você pode preparar o código relevante
antecipadamente. Você pode usar as técnicas que descrevemos no capítulo 4, como refatorar o código
relevante na classe em questão para evitar a pesquisa.

APOIE A MEMÓRIA DE TRABALHO: DESENHE DIAGRAMAS

O Capítulo 4 propôs uma série de técnicas que podem apoiar a memória de trabalho,
incluindo o uso de diagramas. Pode ser difícil para alguém novo em uma base de código criar
esses artefatos. No processo de integração, o integrador pode considerar a criação de tabelas
que auxiliam a memória de trabalho.
No entanto, como descrevemos, os diagramas nem sempre são úteis para iniciantes absolutos
que podem estar relutantes em se afastar do código e ver uma imagem maior. Monitor
a utilidade dos diagramas muitas vezes e abandoná-los quando a técnica não é (ainda)
útil.

13.3.3 Ler o código juntos


Outra técnica que você pode usar para integrar os recém-chegados é ler o código em equipe de forma
colaborativa. No capítulo 5, propusemos sete técnicas da linguagem natural para serem
aplicado à leitura de código:
Machine Translated by Google

Atividades para um melhor processo de onboarding 217

ÿ Ativação— Pensar ativamente em coisas relacionadas para ativar o conhecimento prévio


ÿ Determinar a importância—Decidir quais partes do texto são mais relevantes
ÿ Inferir —Preencher fatos que não são dados explicitamente no texto
ÿ Monitoramento—Acompanhar sua compreensão de um texto
ÿ Visualização—Desenhando diagramas do texto lido para aprofundar a compreensão
ÿ Questionamento—Fazer perguntas sobre o texto em mãos
ÿ Resumindo—Criando um breve resumo do texto

No capítulo 5, propusemos essas atividades como coisas a fazer quando você lê código como um
desenvolvedor individual. No entanto, essas tarefas também podem ser usadas quando você integra um novo
desenvolvedor em uma base de código. Quando a equipe realiza essas atividades, diminui a capacidade do recém-chegado
carga cognitiva, e eles podem então se concentrar no código com mais memória de trabalho.
A seguir, detalharemos como cada uma das sete atividades pode ser usada de forma colaborativa.
código de leitura.

ATIVANDO

Antes do início da sessão de leitura, examine os conceitos relevantes no código. Se você já fez o exercício
13.1, você pode preparar esta lista de antemão. Lembre o recém-chegado de conceitos relevantes de antemão
e alivie a confusão sobre conceitos. Isto é
melhor discutir detalhes neste momento do que quando o recém-chegado também está tentando
entender o código. Por exemplo, se você estiver usando um modelo Model-View-Controller, ele
é melhor se esse fato não for descoberto ao navegar em diferentes arquivos de código.
Após a fase de ativação, a sessão de código de leitura colaborativa pode começar. Observação
que a atividade que está sendo executada é a compreensão. Isso significa que queremos limitar a
outras quatro atividades.

DETERMINANDO A IMPORTÂNCIA

Se você tem pouco conhecimento de algo, pode ser muito difícil distinguir entre
conhecimento básico e o que é menos importante. Indicando as partes mais relevantes
código suporta recém-chegados. Isso pode ser feito em uma leitura colaborativa, na qual, por
Por exemplo, todos os membros da equipe apontam o que eles acham que são as linhas mais relevantes ou
importantes do código. Alternativamente, você pode ter um documento decorrente de sessões anteriores de
leitura de código em que os destaques importantes já foram
apontou, que um recém-chegado pode ler.

INFERINDO

Da mesma forma, pode ser difícil preencher detalhes que não são explicitamente declarados. Por exemplo, um
equipe pode ter entendimentos claros de conceitos de domínio, como remessas
deve sempre conter pelo menos um pedido, mas tais decisões podem não ter sido explicitamente documentadas
no código. Da mesma forma, para determinar a importância, você pode expressamente
apontar descobertas em uma sessão de leitura, ou essas decisões podem ter sido relatadas
para que possam ser facilmente compartilhados com os recém-chegados.
Machine Translated by Google

218 CAPÍTULO 13 Como integrar novos desenvolvedores

MONITORAMENTO

Como descrevemos, a coisa mais importante que você pode fazer no processo de integração é
acompanhar o nível de compreensão que o integrado tem atualmente. Peça-lhes regularmente
para fazer uma rápida recapitulação da leitura, definir conceitos de domínio essenciais ou
recordar um conceito de programação usado no código.
VISUALIZAÇÃO

Conforme descrito anteriormente neste livro, os diagramas servem a dois propósitos. Primeiro,
eles podem apoiar a memória de trabalho (capítulo 4) e, segundo, sua criação pode ajudar na
compreensão (capítulo 5). Dependendo do nível do recém-chegado, o onboarder pode criar um
diagrama e usá-lo para ajudar o onboardee na leitura do código, ou pedir ao recém-chegado que
o desenhe para aprofundar sua compreensão do código.

QUESTIONAMENTO Ao ler o código de forma colaborativa, pergunte e responda regularmente a perguntas sobre ele.
Dependendo do nível do recém-chegado, você pode fazer perguntas que ele responda ou
incentivá-lo a fazer perguntas que você possa responder. Os recém-chegados relativamente
experientes também podem se beneficiar de fazer perguntas para encontrar as respostas, com
sua ajuda, mas sempre monitore sua carga cognitiva em tarefas menos orientadas. Quando o
onboardee começa a adivinhar ou conclui coisas que não fazem sentido, sua carga cognitiva
pode ser excedida.
RESUMO
Como etapa final na leitura de código para integração, você pode escrever um resumo do código
que foi lido em conjunto. Um exemplo de resumo é mostrado na figura 13.4. Dependendo do
estado da documentação na base de código, esse resumo pode ser enviado à base de código
como documentação, o que é uma tarefa ótima e acolhedora para o onboardee fazer após a
sessão. Nesse processo, eles conhecerão o fluxo de trabalho da base de código (por exemplo,
criando um pull request e solicitando uma revisão sobre ele, de uma forma que não seja muito
assustadora ou muito difícil para a memória de trabalho).

Figura 13.4 Um exemplo de resumo de código


Machine Translated by Google

Resumo 219

Resumo ÿ Os

especialistas pensam e agem de forma diferente dos iniciantes. Os especialistas podem raciocinar abstratamente
sobre o código e ter a capacidade de pensar sobre ele sem se referir ao próprio código. Iniciantes tendem a se
concentrar nos detalhes do código e têm dificuldade em voltar atrás dos detalhes. ÿ Quando os programadores
de nível médio aprendem novas informações, eles às vezes voltam

para o pensamento de nível iniciante.

ÿ As pessoas que estão aprendendo um novo conceito precisam aprender tanto em termos abstratos
quanto em exemplos concretos. ÿ As pessoas que estão aprendendo um novo conceito também
precisam de tempo para conectar um novo conceito ao conhecimento prévio. ÿ Ao fazer a integração,
limite as atividades de programação que os recém-chegados realizam a uma

de uma vez.

ÿ Ao fazer a integração, prepare informações relevantes para apoiar as


longo prazo, curto prazo e memória de trabalho.
Machine Translated by Google
Machine Translated by Google

epílogo
Algumas palavras para fechar este livro
Obrigado por chegar ao final deste livro. Se você leu todo o texto ou apenas alguns pedaços aqui
e ali, estou feliz que você tenha chegado a este ponto. Escrever este livro foi uma experiência
muito gratificante. Aprendi muito sobre ciência cognitiva e programação porque me aprofundei no
assunto do que nunca.
Mas também aprendi muito sobre mim. Uma das coisas que aprendo deste livro é que confusão e
se sentir sobrecarregado cognitivamente é bom e faz parte da vida e do aprendizado. Antes de
saber tudo o que sei sobre cognição, costumava ficar chateado comigo mesmo por não ser
inteligente o suficiente para ler artigos complicados ou explorar códigos desconhecidos; agora
posso ser mais gentil comigo mesmo e dizer: “Bem, talvez você tenha muita carga cognitiva”.

Também comecei a trabalhar no que acabou se tornando a linguagem de programação Hedy.


E embora eu certamente não recomende a ninguém criar uma linguagem de programação e
escrever um livro ao mesmo tempo, na minha opinião esses dois processos estavam muito
conectados e combinavam bem tematicamente. Muitas das lições deste livro, como a redução da
carga cognitiva, equívocos, a onda semântica e a repetição espaçada, são descritas neste livro e
também implementadas em Hedy. Se você está ensinando crianças a programar, eu ficaria
honrado se você desse uma chance a Hedy em www.hedycode.com. É gratuito e de código aberto!

Para encerrar este livro, quero enfatizar que gostei imensamente de explorar, resumir e cobrir
o trabalho de grandes cientistas no campo da programação e da cognição. Embora fazer minha
própria pesquisa seja incrível, ajudar os desenvolvedores a entender a pesquisa existente tem
sido tremendamente recompensador e pode ter um impacto maior na programação do que meus
próprios projetos. Se você quiser ler ainda mais, há alguns livros que posso recomendar e alguns
cientistas que você pode querer acompanhar.

Se você quer conhecer melhor seu cérebro, eu adoro o livro Thinking Fast and Slow (Farrar,
Straus, and Giroux, 2013) de Daniel Kahneman porque ajuda você a entender seu cérebro de uma
maneira mais ampla do que este livro. Da mesma forma, o livro

221
Machine Translated by Google

222 Epílogo

How We Learn (Penguin, 2015) de Benedict Carey se aprofunda em tópicos de


repetição e memória. Para leitores especificamente interessados em aprender matemática, o livro
How the Brain Learns Mathematics (Corwin, 2014) de David Sousa está repleto de
pesquisa sobre a aprendizagem de matemática e abstração. No campo da programação, posso
recomendar o livro The Philosophy of Software Design (Yaknyam Press, 2018) de John Ous terhout.
Não é uma leitura fácil, mas está repleta de insights profundos sobre como
softwares de projeto. Eu também amo muito o pequeno livro Software Design Decoded (MIT
Press, 2016) de Andre van der Hoek e Marian Petre, que resume 66 maneiras de pensar dos
especialistas em programação, o que pode ser visto como um baralho de flashcards que você pode aplicar em
várias situações. Eu também cobri este livro em um episódio da SE Radio.
Se você está ansioso para ler artigos científicos sobre tópicos relacionados a este livro, posso
recomendar dois artigos que impactaram amplamente meu pensamento: “Por que a orientação mínima
Durante a Instrução Não Funciona” de Kirschner et al.,1 que desafiou tudo
Eu sabia sobre ensino, e “Toward a Developmental Epistemology of Computer Programming” de
Raymond Lister, que canalizou minha experiência de ensinar programação mal e me ensinou a fazer
melhor.2
Este livro cobre o trabalho de uma série de cientistas incríveis, mas as poucas centenas
As páginas que este livro se tornou certamente não foram suficientes para cobrir todo o seu grande trabalho. Se
você quer se aprofundar na pesquisa de compreensão do programa, você quer seguir
essas ótimas pessoas: Sarah Fakhoury (@fakhourysm), Alexander Serebrenik (@asere brenik), Chris
Parnin (@chrisparnin), Janet Siegmund (@janetsiegmund), Brittany
Johnson (@DrBrittJay), Titus Barik (@barik), David Shepherd(@davidcshepherd),
e Amy Ko (@amyjko).

1
Paul A. Kirschner et al., “Por que a orientação mínima durante a instrução não funciona: uma análise da falha
de Ensino Construtivista, Descoberta, Baseado em Problemas, Experiencial e Baseado em Investigação”,
Psicólogo Educacional, vol. 41, nº. 2, pp. 75–86, 2010, https://www.tandfonline.com/doi/abs/10.1207/
2
s15326985ep4102_1 Raymond Lister, “Toward a Developmental Epistemology of Computer Programming”, 2010, https://
dl.acm.org/doi/10.1145/2978249.2978251
Machine Translated by Google

índice

Símbolos melhorando memórias implícitas 171–172


multitarefa e 189 programação mais rápida com
&& operador 29 + 170
operador 29
B
Numérico
Bartlett, Frederic 43
500 Linhas ou Menos (DiBernardo) 175 beacons 28–30, 87, 129
atividades para iniciantes
UMA para integração 213–218 desenhando
diagramas 216 explicando informações
abreviaturas 137–140
relevantes 215 limitando tarefas 213–214
Abelson, Harold 14 preparando tarefas pequenas e focadas 215–
dimensão de abstração 200
216 lendo código juntos 216–218 comportamento
ativando 84–85, 217 pensamento de 207–211
ativo 42–44 esquemas 43 usando
elaboração 44 atividades 180–
modelo neo-piagetiano para programação 208-210
183 compreensão 181, 203
depuração 182–183 exploração
O modelo original de Piaget 207–208
182, 203 incrementação 181–
esquecendo temporariamente as coisas 210–
182, 203 otimização da base de
211 conceitos, vendo concretamente versus abstratamente
código para atividades
211–213 programadores especialistas versus 83–84 lendo
esperadas 204 pesquisando 180–
código 22–23 postagens de blog 175 livros 175
181, 202–203 transcrição 181, 203 funções anônimas 53

função boxplot() 34, 39 medições


baseadas no cérebro 157
programa APL 7
Áreas de Brodmann 79-80
Apps Húngaro 74–75
Brodmann, Korbinian 79
Arquitetura de Sistemas de Código Aberto, A
(Marrom, Wilson) 175 Brooks, Fred 185
associação 113 Brown, Amy 175
automatização 167–172 bugs 123 equívocos
memórias implícitas ao longo do tempo 168– 117–123 sobre linguagens de
169 fase associativa 169 fase autônoma 169 programação 120–122 depuração com mudança
fase cognitiva 168 conceitual 118–119 diagnóstico na nova base de código
122–123

223
Machine Translated by Google

224 ÍNDICE

bugs (continuação) determinar linhas importantes de código 86–87 inferir


prevenindo ao aprender uma nova linguagem de o significado dos nomes das variáveis 87–88 monitorar
programação 122 suprimindo 119–120 85 questionar 89 resumir o código 90 visualizar 88–89
modelos para pensar sobre 92–94 paradigmas 71–75
nomenclatura e 141–142 benefícios dos papéis 72–73
transferência 111–117
dificuldades de 116–117
benefícios do conhecimento de programação existente
113-114
formas de 114
Notação húngara 73–75
programas 75–78 estágios de
C compreensão 76–78 conhecimento de
texto vs. conhecimento de plano 75 código de
função calcular() 52 padrões leitura 79–84
de chamada 78 processos cognitivos 15–16, 23–31, 79–80
caixa de camelo 140-141
habilidades de linguagem natural vs. 81–84 com
CDCB (dimensões cognitivas de bases de código) 200 iniciantes 216–218 variáveis 68–71
CDN (dimensões cognitivas) 204
atividades e 202–204 compreensão diferentes funções de 68–69
203 exploração 203 incrementação onze funções cobrindo 69–71
203 otimização da base de código clones de código 153 revisões de código
para atividades esperadas 204 129 code smells 148–153 catálogo de
pesquisa 202–203 transcrição 203 propriedades de 192– 148–151 code smells em nível de classe
202 abstração 200 150 code smells em nível de base de
código 150–151 impacto de code
smells 151 em nível de método código
cheira 150
proximidade do mapeamento 197–
198 proximidade do mapeamento, exercício 12,2
prejudicando a cognição 151–153
198 consistência 194 manobras de design 201– clones de código 153 classe
202 difusão 194–195 propensão a erros 193
operações mentais difíceis 198–199 dependências god, método longo 152–153 longa lista
de parâmetros, instruções switch complexas
ocultas 195–196 melhoria da base de código com 152
avaliação progressiva do CDCB 200 196–197
influência de nomes ruins na carga cognitiva 153–
provisoriedade 196 função expressividade 197 159 antipadrões linguísticos 154–155, 158–159
notação secundária 199 viscosidade 196
medindo a carga cognitiva 155–158 medições
visibilidade 200 variável celsius 104 exceções
baseadas no cérebro 157
verificadas 115 chunking 19–21, 25–30 beacons
28–30 clones de código e 152–153 padrões de
EEG (eletroencefalograma) 157 medições
projeto 26 no código 21 praticando 30–31
oculares 156 fNIRS e programação 157–
escrevendo código em partes 25–26 escrevendo
comentários 27–28 pedaços 19 cheiros de código 158
em nível de classe 150 Escala de Paas
155 medições baseadas na pele 156
sinônimos de código 55-56 códigos de nível
de base de código 150-151 bases de código
verificando antipadrões linguísticos em
154-155 diagnosticando equívocos em 122-123
nomeação como parte de 129 consistência com 131

Coders at Work (Seibel) 79


codificando 12 processos
proximidade da dimensão de mapeamento 197–198 cognitivos que afetam 6–12
código 90 interação de 9
aplicar estratégias de compreensão de texto 84–90 ativar LTM (memória de longo prazo) 7
conhecimento prévio 84–85 em relação às tarefas de programação 10–12
Machine Translated by Google

ÍNDICE 225

codificação (continuação) memória de trabalho 56–61


STM (memória de curto prazo) 7–8 combinando gráficos de dependência e tabelas
memória de trabalho 8–9 confusão em de estado 61 gráficos de dependência 56–
4–6 falta de informação 5 falta de 59 memória de curto prazo vs. 48 tabelas de
conhecimento 5 falta de poder de estado 59–60
processamento 6 compilação cognitiva
60 carga cognitiva 48 instruções de comutação complexas
152 sinalizadores compostos 29
compreensão 84–90 ativação de
Germane carrega 173–174 conhecimento prévio 84–85 atividade de
influência de nomes ruins em 153–159 181 determinação de linhas importantes
de código 86–87 dimensões e 203 inferência do
antipadrões linguísticos e 158–159 medindo significado de nomes de variáveis 87–88 monitoramento
155–158 medições baseadas no cérebro 157
85 questionamento 89 código resumido 90 visualização
88–89 conceitos, concretos e abstratos 211–213
EEG (eletroencefalograma) 157 medições
antipadrões conceituais 153 mudança conceitual 118–
oculares 156 fNIRS e programação 157–
119 modelos concretos 98–99 confusão na codificação
158 4–6 falta de informação 5 falta de conhecimento 5
Escala de Paas para carga cognitiva 155 falta de poder de processamento 6 propriedades de
medições baseadas na pele 156
consistência de CDN (dimensões cognitivas) 194 com
técnicas para reduzir 51–56 nomes da base de código 131, 134 contêineres 70
sinônimos de código como acréscimos a flashcards contexto 113 função control() 199 variável de contador
55–56 refatoração 51–52 substituição de
69, 72
construções de linguagem desconhecidas 52–55
tipos de 49–50 carga cognitiva estranha 49–50
carga cognitiva intrínseca 49 fase cognitiva,
memórias implícitas 168 processos cognitivos 6 –12
cheiros de código prejudicando 151–153 clones
de código 153 classe god, método longo 152–153
longa lista de parâmetros, instruções switch complexas
152 interação de 9

Craik, Kenneth 95
atributos críticos 113

D
LTM (memória de longo prazo) 7
código de leitura 15–16, 23–31, 79–80 depuração 182–183
Áreas de Brodmann 79-80 memória declarativa 164
evidências de fMRI 80 prática deliberada 30 código
deslocalizado 51 gráficos de
memória icônica 23–24 em
dependência combinando
relação às tarefas de programação 10–12
tabelas de estado e 61 memória de
STM (memória de curto prazo) 7–8
trabalho e 56–59
com nomeação 133–136 avaliando a
manobras de design 201–202
qualidade dos nomes 136
LTM 134–135 propensão ao erro vs. viscosidade 202
provisoriedade e avaliação progressiva vs. propensão
Nomes de variáveis
ao erro 202 papel expressividade vs. difusão 202
STM 133–134 com informações 135–136
memória de trabalho 8–9 padrões de projeto 26
refatoração cognitiva 52 determinando importância 84, 217
colaboração com colegas 175 código diagramas, desenho 216
complexo 63 técnicas de carga cognitiva DiBernardo, Michael 175
para reduzir 51–56 tipos de 49–50 dimensão de difusão 194-195, 202
razões para dificuldades com 47– interrupções 34-35 documentação 129
50 domínio de aprendizagem 215
Machine Translated by Google

226 ÍNDICE

diagramas de desenho 216 caixa de cobra ou caixa de camelo 140–141


medida de tarefa dupla 188 Fowler, Martin 148
função de codificação diádica 7
G
E
coletor 70
Ebbinghaus, Hermann 38 EEG Carga alemã 173-174
GitHub 175
(eletroencefalogramas) 81, 157 Efeitos de
Beacons, Comentários e Tarefas no Processo de Classe de Deus 150, 152–
Compreensão do Programa em Manutenção de 153 função goo() 153
Software, O (Fan) 27 elaboração 44 emoções 114
codificação 38 propensão a erros 193 provisório e avaliação H
progressiva vs. 202 dimensão de viscosidade vs. 202 função
executeQuery() 198 programadores especialistas operações mentais difíceis 198–199
dimensão de dependências ocultas 195–196
hierarquia, redes vs. 38 transferência de estrada
114 notação húngara 73–75

iniciantes vs. 83–84 eu

leitura de código 22–23


memória explícita 164 memória icônica 23–24
exploração 182, 203 carga memórias implícitas 168–169 fase
cognitiva estranha 49–50 rastreadores associativa 169 fase autônoma
oculares 83 medições oculares 156 169 fase cognitiva 168
melhorando 171–172
importância, determinando 84,
217 incrementação 181–182, 203
F
inferindo 87–88, 217 complexidade
fatores variáveis 69 inerente 49 inibição 120 embutindo teoria
de 52 instâncias 170 interrupções 183–
Fan, Quiyin 27 far 190 em horários convenientes 187–189
transfer 114 função
multitarefa 189–190
fclose() 111
modelo de três etapas de Feitelson 145-146
visão geral 145–146
sucesso de 146
função file.close() 111 função
automatizando e 189
file.open() 197 função filter() pesquisas sobre 190
53 valores fixos 69, 72
preparando-se para 185–187
flashcards 35–37 sinônimos
ajudando a memória prospectiva 186–187
de código 55–56 conjunto de
rotulando subobjetivos 187 armazenando
expansão 36 conjunto de modelo mental 185 retomando após 184 tempo
desbaste 36–37 quando de aquecimento 183–184 carga cognitiva
usar 36 intrínseca 49 variável is_available 70 variável
is_error 70 variável is_set 70, 197 isMember variável
fMRI (ressonância magnética funcional) 80 fNIRS 54–55 é variável válida 154, 159
(espectroscopia de infravermelho próximo funcional) 157–158
ponto focal 76 seguidores 70 função foo() 153 esquecimento 38–
39 curva de esquecimento 38–39 hierarquia vs. redes 38
aprendizagem de novas informações e 210–211 formatação
nomes 137–141 abreviaturas 137–140
J

JAVA
tentando lembrar 17
STM (memória de curto prazo) 8
Machine Translated by Google

ÍNDICE 227

K memórias 164-166
visão geral 165-166
conhecimento desaprender 166
5 ativando conhecimento prévio 84–85 ver informações e 41 repetições
benefícios do conhecimento de programação espaçadas 39–40 fortalecer
existente 113–114 texto vs. plano 75 memórias 42
modelos mentais 94, 97–102
modelos concretos 98–99 no
LTM 100–102 na memória de
eu
trabalho 98, 102 aprendendo
subobjetivos de rotulagem novas 97 máquinas nocionais
187 lambdas 53 máquinas criando conflitantes 107–108 de código-fonte no LTM 101–
102 de código-fonte na memória de trabalho 99–100 visão
nocionais de linguagem e
106–108 geral 96 armazenamento durante interrupções 185
modelos mentais conflitantes 107-108
expandindo conjuntos de máquinas nocionais 106
Meta-programação (Simonyi) 74 cheiros
substituindo construções desconhecidas 52–55 de código no nível de método 150
lambdas 53
variável mínima 121 função Minimum()
compreensões de listas 54
152 equívocos 117–123 sobre linguagens
operadores ternários 54–55
de programação 120–122 depuração
antipadrões linguísticos 154–155, 158–159
com mudança conceitual 118–119 diagnóstico
causando confusão 159 checando na base de
em nova base de código 122–123 prevenção enquanto
código 154–155 compreensões de listas 54
aprende nova programação linguagem 122 suprimir
programação ao vivo 197 método longo 152–153
119–120 modelos 109 para pensar sobre o código 92–
transferência de baixo custo 114
94 modelos mentais 94–102

LTM (memória de longo prazo) 7


Programa APL 7
explicando informações relevantes 215 modelos concretos 98–99
modelos mentais 100–101 na memória de
em LTM 100–101 em LTM e
trabalho e 102 do código-fonte em 101– memória de trabalho 102 em memória de
102
trabalho 98 aprendendo novo 97 de código-
nomes e 134–135
fonte em LTM 101-102 de código-fonte
resolução de problemas, papel em 162– em memória de trabalho 99-100 visão
166 resolução do processo cognitivo questão 162–164 geral 96 máquinas nocionais 102-105 definidos 103
tipos de memórias 164–166 exemplos de 103–104 idiomas e 106–108 níveis de
105 esquemas e 108–109 monitoramento 85, 218
M detentores mais recentes 69–70 detentores mais
procurados 69–70 função multiples() 152 multitarefa
função main() 59, 76–78, 152 domínio 189–190
113 max_benefit_amount 143
max_interest_amount 144 max_points
146 max_prime_factors variável 69
valor máximo 120 memória 37–44
pensamento ativo 42–44 esquemas 43
usando elaboração 44 esquecimento,
razões para 38–39 esquecimento
curva 38– 39 hierarquia versus automatização e 189
redes 38 formas de lembrar pesquisas sobre 190
informações 40–41 força de Homem-Mês Mítico (Brooks) 185
recuperação 41 força de
armazenamento 41 N

nomeando 128–146
escolhendo nomes melhores 142–146
modelo de três etapas de Feitelson 145-146
Machine Translated by Google

228 ÍNDICE

nomeando (continuação) operador 29


moldes de nome 142–144 organizadores 71
aspectos cognitivos de 133-136 Ousterhout, John 185
avaliando a qualidade dos nomes 136
LTM 134–135
P
Nomes de variáveis
STM 133–134 com informações 135–136 Paradigmas da
formatação 137–141 Escala Paas 155 71–
abreviaturas 137–140 snake 75 benefícios das funções 72–73
case ou camel case 140–141
Lista de parâmetros de notação
inferir significado da variável 87–88 influência húngara 73–75, longa 152
nos bugs 141–142 influência na carga cognitiva Petzold, Carlos 75
153–159 impacto duradouro das práticas 131– Filosofia do Design de Software, O (Outerhout) 185
133 perspectivas sobre 129–131 consistência Modelo original de Piaget 207–208
dentro da base de código 131 regras sintáticas planejar conhecimento 75–76, 90
130–131 razões para 129 transferência positiva 115–116 práticas,
nomear 131–133 resolução de problemas
176 automatização 167–172 memórias
forma de documentação 129 parte
implícitas ao longo do tempo 168–169
das bases de código 129 papel nas
melhorar memórias implícitas 171–172
revisões de código 129 servindo
programação mais rápida com 170
como beacons 129 Natureza da
Explicação, O (Craik) 95 transferência próxima elementos de 161
114 transferência negativa 115 modelo neo-
aprendendo com o código e sua explicação 172–175
piagetiano 208-210 redes, hierarquia vs. 38 Carga alemã 173-174
classe de nós 29 máquinas nocionais 102–105
usando exemplos trabalhados na vida profissional 175
definiram 103 exemplos de 103–104 linguagem modelos 109 para pensar sobre o código 92–94 modelos
e 106–108 modelos mentais conflitantes 107–
108 conjuntos em expansão de máquinas mentais 94–102 máquinas nocionais 102–105
nocionais 106 níveis de 105 esquemas e
108–109 máquinas nocionais são semânticas?
papel do LTM com 162–166
109 importância de 108-109
resolvendo a questão do processo cognitivo 162–164
tipos de memórias 164–166 espaço de estado 161–162

poder de processamento
6 linguagens de programação 120–122
Janelas de programação (Petzold) 75
programas 75–78 estágios de compreensão
76–78 conhecimento de texto vs.
O
conhecimento de plano 75 dimensão de
avaliação progressiva 196–197, 202 memória prospectiva
integração de novos desenvolvedores
219 atividades para 213–218 186–187 dimensão de provisoriedade 196, 202 público
classe 8 public static void main 8
desenhando diagramas 216
explicando informações relevantes 215
limitando tarefas 213–214 preparando tarefas
pequenas e focadas 215–216 lendo código juntos
216–218 Q
comportamento dos iniciantes
207-211 modelo neo-piagetiano para programação questionando 89, 218
208-210
O modelo original de Piaget 207–208 R
esquecendo temporariamente as coisas 210–
211 diferença entre ver conceitos concretamente lendo o código 32, 79–84, 216–218
e abstratamente 211-213 ativando 217 fragmentando 19–21, 25–
questões em processo de 206-207 30 beacons 28–30 padrões de projeto
função open() 111, 197 tabelas 26
de operação 88–89
Machine Translated by Google

ÍNDICE 229

lendo código (continuação) no medições baseadas na pele 156 fatias


código 21 de código 77 snake case 140–141
praticando 30–31 código fonte
escrevendo código em partes 25–26
escrevendo comentários 27–28 livros ou posts sobre 175 modelos
processos cognitivos 79–80 mentais na memória de trabalho 99–100 modelos
Áreas de Brodmann 79–80 mentais LTM 101–102 repetição espaçada 39–40
evidências de fMRI 80 espaço de estado 161–162 tabelas de estado combinando
processos cognitivos afetando 23–31 código gráficos de dependência e 61 memória de trabalho e 59–
complexo 63 carga cognitiva 49–56 razões 60 steppers 69, 72 STM (curto memória de termo) 7–8
para dificuldades com 47–50 programa JAVA 8 nomes e 133–134 preparando
pequenas tarefas focadas 215–216 memória de
memória de trabalho 48, 56–61 trabalho vs. 48 força de armazenamento 41 antipadrões
determinando importância 217 estruturais 153 Estrutura e Interpretação de Programas
programadores experientes versus iniciantes 22–23 de Computador (Abelson, Sussman, Sussman) 14
inferindo 217 monitorando 218 habilidades de subobjetivos, rotular 187 resumir 90, 218 suprimir
linguagem natural versus 81–84 iniciantes versus equívocos 119–120 Sussman, Julie 14 Sussman,
programadores especialistas 83–84 rastreadores Gerald Jay 14 instruções switch, sintaxe complexa 152
oculares 83 estratégias para leitura 82–83 45, 109 construir memória 37–44 pensamento ativo 42–
questionando 218 rapidamente lendo 14–18 44 esquecimento, razões para 38–39 formas de lembrar
processos cognitivos quando 15–16 dificuldades de informações 40–41 vendo informações e 41 repetições
18 reexaminando código reproduzido 16–17 resumindo espaçadas 39–40 fortalecendo as memórias 42
218 visualizando 218 refatorando 51–52 Refatorando aprendendo com flashcards 35–37 expandindo o conjunto
(Fowler) 148 refatorando 148 reempacotando 212 36 diminuindo o conjunto 36–37 quando usar 36
código reproduzido 16–17 JAVA 17 segunda
tentativa 17 força de recuperação 41 retrieveElements
() função 159 refatoração reversa 52 função reverse()
163 lembretes de roadblock 184

dimensão de expressividade do papel 197, 202 regras de nomenclatura 130–


papéis da estrutura de variáveis 68 variável raiz 131 lembrando 34–35
28–29 system Húngaro 74–75 Função
System.out.print() 33
S
T
esquemas 43, 108–109
como semântica 109 variável temporária
importância de 108–109 71 variáveis temporárias 71
pesquisando 180–181, 202–203 operadores ternários 54–55
notação secundária dimensão 199 texto
Seibel, Peter 79 estratégias de compreensão 84-90
memória semântica 165 ativando conhecimento prévio 84–85
onda semântica 211 determinando linhas importantes de código 86–87
semântica 109 memória inferindo significado de nomes de variáveis 87–88
sensorial 23 semelhança 113 monitorando 85 questionando 89 resumindo código 90

Simonyi, Carlos 74
Machine Translated by Google

230 ÍNDICE

texto (continuação) Vergleichende Lokalisationslehre der Großhirnrinde


visualização 88–89 (Brodmann) 79
leitura de código vs. 81–84 dimensão de viscosidade 196, 202
programadores iniciantes vs. especialistas 83–84 visibilidade 200 visualizando 88–89,
rastreadores oculares 83 estratégias para leitura 82–83 218
estratégias de compreensão de texto 83 conhecimento
de estrutura de texto 75, 90 função toBinaryString() 4–5, 8, C
47– 48 rastreamento 9, 60 transcrição 181, 203 transferência
111–117 dificuldades de 116–117 durante a aprendizagem caminhantes

111 benefícios do conhecimento de programação existente 70 tempo de aquecimento 183–


113–114 formas de 114 transferência de alto e baixo caminho 184 Wilson, Greg 175 exemplos
114 de vida profissional 175 colaborar
com colegas 175 explorar o GitHub 175
ler livros ou postagens de blog sobre
código-fonte 175 memória de trabalho 8–9, 56–61 programa
BASIC 9 combinando gráficos de dependência e estado tabelas 61
gráficos de dependência 56–59 desenhando diagramas 216
modelos mentais em 98 LTM e 102 de código fonte 99–100
transferência para perto e para longe 114 capacidade de sobrecarga de 152 memória de curto prazo vs. 48
transferência positiva 115–116 de tabelas de estado 59–60
aprendizagem 112 função transform()
52 variável de árvore 28–29

Nome da variável de árvore 87


tartaruga 103

sistemas de tipo 193


escrevendo código 190
você atividades durante a programação 180–183
compreensão 181 depuração 182–183
Über das Gedächtnis (Memória A Contribuição para exploração 182 incrementação 181–182 pesquisa
Psicologia Experimental), (Ebbinghaus) 39 180–181 transcrição 181 código em partes 25–
desempacotando 212 variável de limite superior 69 26 comentários 27–28 interrupções 183–190 em
horários convenientes 187–189 multitarefa 189–
190 preparando-se para 185–187 retomando
V após 184 do tempo de aquecimento 183–184

variáveis 68-71
diferentes papéis de 68–69 onze

papéis cobrindo 69–71 inferindo o


significado dos nomes 87–88 nomes contendo
informações 135–136 letras simples usadas como
138–140
Machine Translated by Google

ENGENHARIA DE SOFTWARE

“Um ótimo livro com insights


O CÉREBRO do programador profundos sobre a ponte
Felinne Hermans entre programação e ”
a mente humana.
aproveite os processos naturais do seu cérebro para ser um

T programador melhor. Técnicas baseadas na ciência cognitiva possibilitam


aprender novos idiomas mais rapidamente, melhorar
—Mike Taylor, CGI

produtividade, reduzir a necessidade de reescritas de código e muito mais. “Ajuda você a entender
Este livro exclusivo irá ajudá-lo a alcançar esses ganhos. como seu cérebro funciona e
como você pode usá-lo de
O Cérebro do Programador desbloqueia a maneira como pensamos sobre código.
Ele oferece técnicas cientificamente sólidas que podem melhorar
forma mais eficaz para ler, escrever e
radicalmente a maneira como você domina novas tecnologias, compreende
código e memoriza sintaxe. Você aprenderá como se beneficiar da luta
colaborar no código.
—Ben McNamara, DataGeek

produtiva e transformar a confusão em uma ferramenta de aprendizado.
Ao longo do caminho, você descobrirá como criar recursos de estudo à “Ensina hábitos baseados na
medida que se tornar um especialista em ensinar a si mesmo e atualizar ciência para reduzir sua carga
novos colegas.
de trabalho mental e abrir
caminho para se tornar um
O que há dentro

• Entenda como seu cérebro vê o código


programador rockstar!
—Daniela Zapata Riesco

• Habilidades de leitura rápida para aprender código rapidamente Finanças M1

• Técnicas para desvendar códigos complexos

• Dicas para tornar as bases de código compreensíveis “Se você já se perguntou


como deve ser trabalhar de forma
Para programadores que têm experiência em trabalhar em mais de um idioma. mais inteligente em vez de mais
difícil, deveria ler este livro. Já
estou vendo melhorias no meu
A Dra. Felienne Hermans é professora associada da Universidade de Leiden, na
dia a dia de trabalho.
Holanda. Ela passou a última década pesquisando programação, como aprender
e como ensiná-la.
—Zhijun Liu, Mediaocean

Registre este livro impresso para obter acesso gratuito a todos os formatos de e-book.
Visite https://www.manning.com/freebook
Ver primeira página

ISBN: 978-1-61729-867-7

TRIPULAÇÃO $ 49,99 / lata $ 65,99 [INCLUINDO e-BOOK]

Você também pode gostar