Storytelling sobre carga cognitiva em times de tecnologia: um caso real
Olá!
Hoje eu gostaria de compartilhar um aprendizado de análise de dados que acredito que possa ser útil na comunicação de alguns problemas comuns em times de tecnologia.
O que é carga cognitiva?
É uma teoria relacionada ao aprendizado e foi popularizada pelo psicólogo John Sweller na década de 1980. Ela possui a seguinte definição:
Carga cognitiva refere-se ao esforço mental total necessário para processar informações, especialmente em contextos que envolvem raciocínio e tomada de decisões. Esse conceito é fundamental para entender como as pessoas aprendem e interagem com as informações, considerando as limitações da memória de trabalho, que só consegue armazenar uma quantidade limitada de informações de cada vez.
De uma maneiral geral, essa teoria é facilmente relacionável com o contexto de desenvolvimento de software. Os tipos de carga existentes são:
Intrínseca: Refere-se à complexidade inerente de uma tarefa ou conceito. Essa carga é o esforço mental necessário para entender e processar informações específicas. No contexto de software, é a complexidade inerente ao domínio em questão. A recomendação é que ela seja simplificada, mas é impossível eliminá-la completamente.
Extrínseca: Este tipo é causado por fatores externos que não contribuem para a aprendizagem. Em contexto de software, podem ser interfaces confusas, dívidas técnicas que complicam a operação, ausência de processos maduros de desenvolvimento e similares. Os jovens chamam isso de Toil. A recomendação para esse tipo é reduzir o máximo possível.
Pertinente: Esta carga é relacionada ao conhecimento relevante que a pessoa deve processar para atingir os objetivos de aprendizagem. É a carga que ajuda na construção de esquemas mentais úteis e está alinhada com o que é pertinente ao aprendizado. Também pode ser chamada de Germânica. Como exemplo, temos: estudar para obter uma boa compreensão dos fundamentos de algoritmos, sistemas distribuídos e outras práticas relevantes ao ofício de desenvolvimento. Ou seja, é basicamente o que eu costumo escrever por aqui. A recomendação para esse tipo é maximizar.
Mas o que uma teoria de aprendizado tem a ver com software? Bom, praticamente tudo. A expressão “dar manutenção em um sistema”, basicamente tem como pré-requisito você aprender e entender como aquele sistema/linguagem/tooling funciona.
Sem isso, tudo acaba sendo prejudicado. Por isso que interrupções e demais ações que são consideradas danosas para o aprendizado de uma maneira geral, prejudicam grandemente a produtividade de uma pessoa desenvolvedora. Porém, antes que eu acabe reescrevendo os argumentos do Team Topologies aqui, gostaria de compartilhar uma ideia que apliquei para explicar o que é carga cognitiva e que achei os resultados satisfatórios.
Contexto
Stakeholders não técnicos não sabem muito bem o que é carga cognitiva, logo, cabe a nós — profissionais de desenvolvimento — explicar para eles. Uma das formas que encontrei de ilustrar com números, foi literalmente, contar linhas de código por sistema.
Eu queria resolver a seguinte situação: um time específico está com muitos sistemas sob sua responsabilidade. Logo, como explicar para pessoas de outras áreas que isso é um problema devido a alta carga cognitiva?
Com alguns passos simples e um pouco de paciência junto ao chatgpt, saiu algo útil. Vou compartilhar o meu processo nos passos abaixo, espero que te ajude, caso passe por um problema semelhante.
Step 1: Download dos repos
Comecei baixando todos os repositórios de código da empresa localmente.
Detalhes importantes: usar o fine-grained tokens do Github não funcionou para baixar repositórios privados. No momento que escrevo isso (jan/25), a feature está em preview, então estou assumindo que pode ser algum bug. Usei o token do modo classic e deu bom.
O script de download é bem simples, ele basicamente se conecta à API do github, lista os repos privados e vai executando o comando git clone em cada um, salvando os arquivos em um diretório local:
import os
import subprocess
import requests
def clone_github_repos(org_name, output_dir, token=None):
"""
Clones all private repositories from a GitHub organization.
Args:
org_name (str): The name of the GitHub organization.
output_dir (str): Directory where repositories will be cloned.
token (str, optional): GitHub personal access token for authentication (required for private repositories).
"""
if not token:
print("A GitHub token is required to fetch private repositories.")
return
headers = {"Authorization": f"Bearer {token}"}
api_url = f"https://api.github.com/orgs/{org_name}/repos"
params = {"per_page": 100, "page": 1, "type": "private"}
repos = []
# Fetch all private repositories from the organization
while True:
response = requests.get(api_url, headers=headers, params=params)
if response.status_code != 200:
print(f"Error fetching repositories: {response.status_code} - {response.text}")
return
data = response.json()
if not data:
break
repos.extend(data)
params["page"] += 1
if not os.path.exists(output_dir):
os.makedirs(output_dir)
os.chdir(output_dir)
# Clone each repository
for repo in repos:
clone_url = repo["clone_url"]
repo_name = repo["name"]
print(f"Cloning {repo_name}...")
try:
subprocess.run(["git", "clone", clone_url], check=True)
except subprocess.CalledProcessError as e:
print(f"Failed to clone {repo_name}: {e}")
if __name__ == "__main__":
org_name = input("Enter the GitHub organization name: ")
output_dir = input("Enter the directory to clone repositories into: ")
token = input("Enter your GitHub token: ").strip()
clone_github_repos(org_name, output_dir, token)
Agora que temos todos os arquivos necessários, podemos ir para o próximo passo.
Step 2: Contagem
Com o pré-requisito atendido, podemos começar a efetuar algumas análises.
O script abaixo conta quantas linhas de código cada pasta/repositório possuem e tem como resultado gerar um CSV com essas informações.
import os
import csv
def summarize_base_folders(directory, output_csv):
# Dictionary to hold line counts per base folder
base_folder_summaries = {}
# Traverse the directory
for root, _, files in os.walk(directory):
# Get the base folder name (relative to the input directory)
base_folder_name = os.path.relpath(root, directory).split(os.sep)[0]
if base_folder_name not in base_folder_summaries:
base_folder_summaries[base_folder_name] = 0
for file in files:
file_path = os.path.join(root, file)
try:
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
base_folder_summaries[base_folder_name] += sum(1 for _ in f)
except Exception as e:
print(f"Error reading file {file_path}: {e}")
# Write summaries to CSV
with open(output_csv, 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.writer(csvfile, delimiter=';')
writer.writerow(["base_folder_name", "line_count", "owner"])
for base_folder_name, line_count in base_folder_summaries.items():
writer.writerow([base_folder_name, line_count, ""])
if __name__ == "__main__":
# Specify the directory and output CSV file
input_directory = input("Enter the directory to summarize: ").strip()
output_csv_file = input("Enter the name of the output CSV file: ").strip()
summarize_base_folders(input_directory, output_csv_file)
print(f"Summary CSV has been generated: {output_csv_file}")
Eu adicionei uma coluna de Owner vazia para cada linha para facilitar, o motivo disso está no próximo passo.
Step 3: Classificação
Tendo os dados nos formato mencionado, agora que vem a parte trabalhosa. Classificar cada sistema por owner. Isso vai depender de quantos repositórios sua organização possui, obviamente.
Apesar de trabalhoso, foi uma análise interessante, ajuda a achar repois inutéis que podem ser deletados, sistemas com mais de um dono (A.K.A Shared Kernel) e similares.
O que irá gerar algo parecido com isso (os dados são fícticios):
project,line_count,owner
backend,2063,Owner A
mobile,2534,Owner B
backend_c#,11583,Owner C
java_legacy,12182,Owner D
crypto_libs,12231,Owner A
front_legacy,29791,Owner A
new_front,31462,Owner A
utils,189908,Owner E
iac_scripts,255099,Owner B
configs,555530,Owner F
Step 4: Visualização de dados
Com os dados classificados, você pode exibir os resultados de diversas formas. Porém, um formato que eu recomendo é o do script abaixo, que faz uma agregação por linhas/owners.
import pandas as pd
import matplotlib.pyplot as plt
# Load the data from the CSV file
csv_file = 'data.csv' # Replace with your actual CSV file name
data = pd.read_csv(csv_file, sep=',')
# Ensure the necessary columns exist
if not {'project', 'line_count', 'owner'}.issubset(data.columns):
raise ValueError("The CSV file must contain 'project', 'line_count', and 'owner' columns.")
df = pd.DataFrame(data)
# Group by owner and aggregate
summary = df.groupby('owner').agg(
total_lines=('line_count', 'sum'),
total_projects=('project', 'count')
).reset_index()
print(summary)
# Plot total lines and projects per owner
fig, ax1 = plt.subplots(figsize=(8, 5))
# Bar chart for total projects
ax1.bar(summary['owner'], summary['total_projects'], color='blue', label='Total Projects', alpha=0.7)
ax1.set_ylabel('Total Projects', color='blue')
# Line chart for total lines
ax2 = ax1.twinx()
ax2.plot(summary['owner'], summary['total_lines'], color='orange', label='Total Lines', marker='o')
ax2.set_ylabel('Total Lines', color='orange')
# Chart details
plt.title('Projects and Lines per Owner')
ax1.set_xlabel('Owner')
plt.show()
O script tem como output um resuminho e um gráfico:
É um gráfico de barras e linhas. No caso, as linhas estão em laranja e os sistemas (repos) em azul:
Nesse exemplo, é possível interpretar que há uma power law (já comentei sobre isso aqui) em ação. O que indica que um único time (owner A), é responsável por 40% de todos os sistemas. Porém, o time owner F é responsável pelo sistema maior em quantidade de linhas, o que pode ou não ser algo relevante. De uma maneira geral, esse comportamento de poucas causas para muitos efeitos é um sinal de que algo está errado e precisa ser revisto. Geralmente, algo relacionado a cultura do herói.
Obviamente que transformei os dados reais em dados fícticios aqui para não expor a empresa que trabalhei, mas eu me deparei com o mesmo cenário quando fiz essa análise em um caso real.
Step 5: Comunicação
Com esse tipo de visualização em mãos, agora é mostrar e explicar para as pessoas, com base nos dados, porque isso é um problema.
Lembre-se de evitar jargões técnicos para pessoas não técnicas, tratar outras pessoas com respeito, ouvir bastante e demais boas práticas de comunicação.
Com isso, espero que você consiga argumentar com sucesso que a estrutura de times está precisando de mudanças.
Lembrando que esse formato de storytelling é o mínimo recomendado para qualquer pessoa que queira gerar mudanças reais em qualquer empresa. Seja o mais data-driven que você puder ser, afinal:
Espero que o texto seja útil para você de alguma forma.
Até!
Você gostou do conteúdo e gostaria de fazer mentoria comigo? Clique aqui e descubra como.