---
title: "Portfólio no GitHub para Desenvolvedores Rust"
url: "https://rustlang.com.br/carreira/portfolio-github/"
markdown_url: "https://rustlang.com.br/carreira/portfolio-github.MD"
description: "Como montar um portfólio GitHub que impressiona recrutadores de vagas Rust. Projetos recomendados, estrutura de README, CI/CD com GitHub Actions, profile README e estratégias de contribuição."
date: ""
author: ""
---

# Portfólio no GitHub para Desenvolvedores Rust

Como montar um portfólio GitHub que impressiona recrutadores de vagas Rust. Projetos recomendados, estrutura de README, CI/CD com GitHub Actions, profile README e estratégias de contribuição.


# Portfólio no GitHub para Desenvolvedores Rust

Seu perfil no GitHub é, para muitos recrutadores e tech leads de Rust, mais importante que o próprio currículo. Em uma linguagem onde a comunidade open source é central e o código fala por si, um GitHub bem organizado pode abrir portas que nenhum diploma consegue. Este guia ensina como transformar seu perfil GitHub em uma vitrine profissional, desde a escolha dos projetos até a configuração de CI/CD e a otimização do seu profile README.

---

## Por Que o GitHub é Tão Importante para Vagas Rust

A comunidade Rust nasceu no open source e valoriza profundamente a qualidade de código. Quando um recrutador ou tech lead avalia um candidato, o GitHub responde a perguntas que o currículo não consegue:

- **Essa pessoa realmente sabe Rust?** O código mostra a verdade
- **Como ela lida com erros?** Usa `unwrap()` em tudo ou trata erros adequadamente?
- **Ela escreve testes?** Tem suite de testes organizada?
- **Como organiza o código?** A estrutura de módulos faz sentido?
- **Ela documenta?** READMEs e doc comments estão presentes?
- **Ela colabora?** Tem PRs em outros projetos, responde issues?

---

## Projetos que Impressionam Recrutadores

### Critérios de um bom projeto de portfólio

Um projeto que vai impressionar precisa ter:

1. **Propósito claro**: resolve um problema real ou demonstra uma habilidade específica
2. **Código limpo e idiomático**: segue as convenções da comunidade Rust
3. **Testes**: unitários, de integração e possivelmente benchmarks
4. **Documentação**: README completo, doc comments, exemplos de uso
5. **CI/CD**: builds e testes automatizados que passam
6. **Commits organizados**: histórico legível com mensagens descritivas

### Projetos recomendados por nível

**Para Júnior (3-4 projetos)**

| Projeto | O que demonstra | Complexidade |
|---------|-----------------|-------------|
| CLI tool (grep, wc, find) | I/O, args parsing, error handling | Baixa |
| API REST com banco de dados | Web, async, SQL, serialização | Média |
| Chat TCP/UDP | Networking, concorrência, protocolos | Média |
| Conversor/Parser de formatos | Parsing, serde, file I/O | Baixa-Média |

**Para Pleno (2-3 projetos mais substanciais)**

| Projeto | O que demonstra | Complexidade |
|---------|-----------------|-------------|
| Microserviço completo com Docker | Arquitetura, deployment, observability | Alta |
| Biblioteca publicada no crates.io | Design de API, documentação, versionamento | Alta |
| Sistema de cache/storage | Estruturas de dados, performance, benchmarks | Alta |

**Para Sênior (1-2 projetos ambiciosos)**

| Projeto | O que demonstra | Complexidade |
|---------|-----------------|-------------|
| Engine/Runtime/Compilador | Conhecimento profundo, design de sistemas | Muito alta |
| Contribuições significativas a projetos populares | Colaboração, código de produção | Variável |

### Exemplo de projeto CLI impressionante

Veja como estruturar um projeto que demonstra competência:

```
meu-projeto-cli/
├── Cargo.toml
├── README.md
├── LICENSE
├── .github/
│   └── workflows/
│       └── ci.yml
├── src/
│   ├── main.rs
│   ├── lib.rs
│   ├── cli.rs
│   ├── config.rs
│   ├── error.rs
│   └── processor/
│       ├── mod.rs
│       ├── parser.rs
│       └── formatter.rs
├── tests/
│   ├── integration_test.rs
│   └── fixtures/
│       ├── input_valido.txt
│       └── input_invalido.txt
├── benches/
│   └── benchmark.rs
└── examples/
    └── uso_basico.rs
```

Arquivo `src/error.rs` demonstrando tratamento de erros idiomático:

```rust
use std::fmt;
use std::io;

/// Erros possíveis durante o processamento de arquivos
#[derive(Debug)]
pub enum AppError {
    /// Erro de leitura/escrita de arquivo
    Io(io::Error),
    /// Formato de entrada inválido
    FormatoInvalido {
        linha: usize,
        detalhes: String,
    },
    /// Configuração ausente ou inválida
    Config(String),
}

impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AppError::Io(e) => write!(f, "Erro de I/O: {}", e),
            AppError::FormatoInvalido { linha, detalhes } => {
                write!(f, "Formato inválido na linha {}: {}", linha, detalhes)
            }
            AppError::Config(msg) => write!(f, "Erro de configuração: {}", msg),
        }
    }
}

impl std::error::Error for AppError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            AppError::Io(e) => Some(e),
            _ => None,
        }
    }
}

impl From<io::Error> for AppError {
    fn from(e: io::Error) -> Self {
        AppError::Io(e)
    }
}
```

Arquivo `src/lib.rs` com documentação e testes:

```rust
//! # Meu Processador de Dados
//!
//! Biblioteca para processamento e transformação de arquivos
//! de dados estruturados.
//!
//! ## Exemplo de uso
//!
//! ```rust
//! use meu_processador::Processador;
//!
//! let proc = Processador::new();
//! let resultado = proc.processar("dados.csv").unwrap();
//! println!("Processados {} registros", resultado.total);
//! ```

pub mod config;
pub mod error;
pub mod processor;

pub use error::AppError;

/// Resultado do processamento contendo estatísticas
#[derive(Debug, Clone)]
pub struct Resultado {
    /// Total de registros processados
    pub total: usize,
    /// Registros que passaram na validação
    pub validos: usize,
    /// Registros com erros
    pub invalidos: usize,
    /// Tempo de processamento em milissegundos
    pub tempo_ms: u128,
}

/// Processador principal de dados
pub struct Processador {
    config: config::Config,
}

impl Processador {
    /// Cria um novo processador com configuração padrão
    pub fn new() -> Self {
        Self {
            config: config::Config::default(),
        }
    }

    /// Cria um processador com configuração customizada
    pub fn with_config(config: config::Config) -> Self {
        Self { config }
    }

    /// Processa um arquivo e retorna o resultado
    ///
    /// # Errors
    ///
    /// Retorna `AppError::Io` se o arquivo não puder ser lido
    /// Retorna `AppError::FormatoInvalido` se o formato for inválido
    pub fn processar(&self, caminho: &str) -> Result<Resultado, AppError> {
        let inicio = std::time::Instant::now();
        let conteudo = std::fs::read_to_string(caminho)?;

        let mut validos = 0;
        let mut invalidos = 0;

        for (i, linha) in conteudo.lines().enumerate() {
            if self.validar_linha(linha) {
                validos += 1;
            } else {
                invalidos += 1;
                if self.config.strict {
                    return Err(AppError::FormatoInvalido {
                        linha: i + 1,
                        detalhes: format!("Linha inválida: {}", linha),
                    });
                }
            }
        }

        Ok(Resultado {
            total: validos + invalidos,
            validos,
            invalidos,
            tempo_ms: inicio.elapsed().as_millis(),
        })
    }

    fn validar_linha(&self, linha: &str) -> bool {
        !linha.trim().is_empty() && linha.contains(self.config.delimitador)
    }
}

impl Default for Processador {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Write;
    use tempfile::NamedTempFile;

    fn criar_arquivo_temp(conteudo: &str) -> NamedTempFile {
        let mut arquivo = NamedTempFile::new().unwrap();
        write!(arquivo, "{}", conteudo).unwrap();
        arquivo
    }

    #[test]
    fn test_processar_arquivo_valido() {
        let arquivo = criar_arquivo_temp("campo1,campo2\nvalor1,valor2\n");
        let proc = Processador::new();
        let resultado = proc.processar(arquivo.path().to_str().unwrap()).unwrap();

        assert_eq!(resultado.total, 2);
        assert_eq!(resultado.validos, 2);
        assert_eq!(resultado.invalidos, 0);
    }

    #[test]
    fn test_processar_com_linhas_invalidas() {
        let arquivo = criar_arquivo_temp("campo1,campo2\nlinha_sem_virgula\n");
        let proc = Processador::new();
        let resultado = proc.processar(arquivo.path().to_str().unwrap()).unwrap();

        assert_eq!(resultado.total, 2);
        assert_eq!(resultado.validos, 1);
        assert_eq!(resultado.invalidos, 1);
    }

    #[test]
    fn test_arquivo_inexistente() {
        let proc = Processador::new();
        let resultado = proc.processar("/caminho/inexistente.csv");

        assert!(resultado.is_err());
        assert!(matches!(resultado, Err(AppError::Io(_))));
    }
}
```

---

## Estrutura de README para Projetos Rust

O README é a primeira coisa que as pessoas veem. Siga esta estrutura:

### Template de README

```markdown
# Nome do Projeto

Breve descrição em uma frase do que o projeto faz.

[![CI](https://github.com/usuario/projeto/actions/workflows/ci.yml/badge.svg)](https://github.com/usuario/projeto/actions)
[![Crates.io](https://img.shields.io/crates/v/nome-do-crate)](https://crates.io/crates/nome-do-crate)
[![Documentation](https://docs.rs/nome-do-crate/badge.svg)](https://docs.rs/nome-do-crate)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Funcionalidades

- Funcionalidade 1: descrição breve
- Funcionalidade 2: descrição breve
- Funcionalidade 3: descrição breve

## Instalação

### Via cargo

    cargo install nome-do-projeto

### Compilar do fonte

    git clone https://github.com/usuario/projeto
    cd projeto
    cargo build --release

## Uso

### Exemplo básico

    nome-do-projeto arquivo.txt --opcao valor

### Como biblioteca

    use nome_do_projeto::Processador;

    let proc = Processador::new();
    let resultado = proc.processar("dados.csv")?;

## Arquitetura

Breve descrição da arquitetura do projeto.

## Contribuindo

Contribuições são bem-vindas! Veja CONTRIBUTING.md.

## Licença

MIT - veja LICENSE.
```

### Dicas para um README eficaz

1. **Comece com o propósito**: o que o projeto faz e por que ele existe
2. **Inclua badges**: CI status, versão no crates.io, licença
3. **Mostre exemplos de uso**: código funcional que pode ser copiado e rodado
4. **Documente a instalação**: passos claros para começar a usar
5. **Inclua screenshots/GIFs**: para ferramentas CLI, mostre o output
6. **Mantenha atualizado**: README desatualizado é pior que README simples

---

## CI/CD com GitHub Actions para Rust

Um projeto com CI configurado demonstra profissionalismo. Este é um workflow completo:

### Workflow básico (recomendado para todos os projetos)

Crie o arquivo `.github/workflows/ci.yml`:

```yaml
name: CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

env:
  CARGO_TERM_COLOR: always
  RUSTFLAGS: -Dwarnings

jobs:
  check:
    name: Check
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - run: cargo check --all-features

  fmt:
    name: Rustfmt
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt
      - run: cargo fmt --all -- --check

  clippy:
    name: Clippy
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy
      - uses: Swatinem/rust-cache@v2
      - run: cargo clippy --all-targets --all-features

  test:
    name: Test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - run: cargo test --all-features

  docs:
    name: Docs
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - run: cargo doc --no-deps --all-features
        env:
          RUSTDOCFLAGS: -Dwarnings
```

### Workflow avançado (para projetos mais maduros)

```yaml
name: CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

env:
  CARGO_TERM_COLOR: always

jobs:
  test:
    name: Test (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        rust: [stable, beta]
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@master
        with:
          toolchain: ${{ matrix.rust }}
      - uses: Swatinem/rust-cache@v2
      - run: cargo test --all-features --verbose

  msrv:
    name: Minimum Supported Rust Version
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@master
        with:
          toolchain: "1.75.0"  # seu MSRV
      - uses: Swatinem/rust-cache@v2
      - run: cargo check --all-features

  coverage:
    name: Code Coverage
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: Install cargo-tarpaulin
        run: cargo install cargo-tarpaulin
      - name: Generate coverage
        run: cargo tarpaulin --out xml
      - name: Upload to codecov
        uses: codecov/codecov-action@v3
        with:
          file: cobertura.xml

  security:
    name: Security Audit
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: rustsec/audit-check@v2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
```

### Workflow de release automatizado

```yaml
name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  build:
    name: Build (${{ matrix.target }})
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        include:
          - target: x86_64-unknown-linux-gnu
            os: ubuntu-latest
          - target: x86_64-apple-darwin
            os: macos-latest
          - target: x86_64-pc-windows-msvc
            os: windows-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}
      - run: cargo build --release --target ${{ matrix.target }}
      - uses: actions/upload-artifact@v4
        with:
          name: binary-${{ matrix.target }}
          path: target/${{ matrix.target }}/release/meu-projeto*

  publish:
    name: Publish to crates.io
    runs-on: ubuntu-latest
    needs: build
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - run: cargo publish
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
```

---

## Profile README: Sua Vitrine Pessoal

O Profile README aparece na página principal do seu GitHub. Crie um repositório com seu nome de usuário e adicione um `README.md`.

### Exemplo de Profile README para dev Rust

```markdown
# Olá! Sou [Seu Nome] 🦀

Desenvolvedor(a) Rust apaixonado(a) por sistemas de alta performance
e código seguro.

## Sobre mim

- Desenvolvedor(a) Rust com X anos de experiência
- Foco em backend, sistemas distribuídos e ferramentas CLI
- Contribuidor(a) open source ativo(a)
- Baseado(a) em [Cidade], Brasil

## Tecnologias

**Principal:** Rust

**Ecossistema Rust:** Tokio, Axum, Serde, SQLx, Clap, Tracing

**Também trabalho com:** PostgreSQL, Redis, Docker, AWS, GitHub Actions

## Projetos em destaque

| Projeto | Descrição | Status |
|---------|-----------|--------|
| [projeto-1](link) | Descrição curta | Em desenvolvimento |
| [projeto-2](link) | Descrição curta | Estável |
| [projeto-3](link) | Descrição curta | Publicado no crates.io |

## Contribuições Open Source

- [tokio](https://github.com/tokio-rs/tokio) - Melhoria de performance em X
- [serde](https://github.com/serde-rs/serde) - Documentação e exemplos

## Contato

- LinkedIn: [seu-linkedin](link)
- Blog: [seu-blog](link)
- Email: seu@email.com
```

### Dicas para o Profile README

1. **Seja conciso**: ninguém vai ler um README de 200 linhas no seu perfil
2. **Destaque Rust**: deixe claro que é sua linguagem principal
3. **Link para projetos**: facilite a navegação até seus melhores trabalhos
4. **Mantenha atualizado**: perfil desatualizado passa má impressão
5. **Evite widgets excessivos**: badges de streak e stats ficam poluídos se forem muitos
6. **Mostre personalidade**: é um espaço profissional, mas pode ter seu toque pessoal

---

## Organizando Seus Repositórios

### Fixar repositórios estratégicos

O GitHub permite fixar até 6 repositórios no seu perfil. Escolha estrategicamente:

1. **Seu melhor projeto Rust completo** (com testes, CI, README)
2. **Uma contribuição a projeto popular** (fork com PR aceito)
3. **Uma biblioteca/crate publicada** (se tiver)
4. **Um projeto que demonstre habilidade específica** (async, CLI, web)
5. **Profile README** (se estiver bem feito)
6. **Algo criativo ou divertido** (demonstra paixão)

### Limpeza de repositórios

- **Archive** projetos antigos ou abandonados que não representam seu nível atual
- **Torne privados** exercícios de curso ou código de baixa qualidade
- **Delete** forks que você nunca modificou
- **Adicione descrições** em todos os repositórios públicos
- **Adicione tópicos/tags** (rust, cli, web, async, etc.)

---

## Gráfico de Contribuições e Atividade

### O que recrutadores olham

O gráfico de contribuições (os quadradinhos verdes) não é tudo, mas conta uma história:

- **Consistência**: atividade regular é melhor que picos esporádicos
- **Tipo de atividade**: commits, PRs, issues e reviews contam
- **Profundidade**: contribuições significativas pesam mais que commits triviais

### Estratégias para manter atividade consistente

1. **Comprometa-se com 30 minutos por dia**: pequenos commits diários mantêm o gráfico verde
2. **Tenha um projeto ativo**: sempre trabalhe em pelo menos um projeto pessoal
3. **Revise issues em projetos que usa**: triagem de issues conta como contribuição
4. **Documente seu aprendizado**: commits de documentação também contam
5. **Participe de code reviews**: review de PRs de terceiros mostra colaboração

### O que NÃO fazer

- Não faça commits vazios ou artificiais só para deixar o gráfico verde
- Não commite código ruim apressadamente
- Não force atividade quando não há o que contribuir genuinamente
- Não se estresse com o gráfico; qualidade importa mais

---

## Escrevendo Doc Comments de Qualidade

Doc comments em Rust aparecem na documentação gerada pelo `cargo doc` e no docs.rs. Escrever boa documentação é um diferencial enorme.

```rust
/// Calcula a média ponderada de um conjunto de valores.
///
/// Cada valor é acompanhado de um peso. A média é calculada como
/// a soma dos produtos (valor * peso) dividida pela soma dos pesos.
///
/// # Arguments
///
/// * `valores` - Slice de tuplas (valor, peso)
///
/// # Returns
///
/// Retorna `Some(media)` se houver valores, ou `None` se o slice
/// estiver vazio ou a soma dos pesos for zero.
///
/// # Examples
///
/// ```
/// use minha_lib::media_ponderada;
///
/// let valores = vec![(8.0, 2.0), (9.0, 3.0), (7.0, 1.0)];
/// let media = media_ponderada(&valores).unwrap();
/// assert!((media - 8.33).abs() < 0.01);
/// ```
///
/// ```
/// use minha_lib::media_ponderada;
///
/// // Retorna None para entrada vazia
/// assert_eq!(media_ponderada(&[]), None);
/// ```
pub fn media_ponderada(valores: &[(f64, f64)]) -> Option<f64> {
    if valores.is_empty() {
        return None;
    }

    let soma_ponderada: f64 = valores.iter()
        .map(|(v, p)| v * p)
        .sum();

    let soma_pesos: f64 = valores.iter()
        .map(|(_, p)| *p)
        .sum();

    if soma_pesos == 0.0 {
        None
    } else {
        Some(soma_ponderada / soma_pesos)
    }
}
```

---

## Publicando no crates.io

Ter uma crate publicada no crates.io é um excelente diferencial.

### Checklist antes de publicar

```toml
# Cargo.toml completo para publicação
[package]
name = "minha-crate"
version = "0.1.0"
edition = "2021"
authors = ["Seu Nome <seu@email.com>"]
description = "Descrição curta e clara da crate"
license = "MIT"
repository = "https://github.com/usuario/minha-crate"
homepage = "https://github.com/usuario/minha-crate"
documentation = "https://docs.rs/minha-crate"
readme = "README.md"
keywords = ["rust", "keyword2", "keyword3"]
categories = ["command-line-utilities"]
rust-version = "1.75.0"  # MSRV

[badges]
maintenance = { status = "actively-developed" }
```

### Passos para publicar

```bash
# 1. Verificar se tudo está correto
cargo publish --dry-run

# 2. Rodar todos os testes
cargo test --all-features

# 3. Verificar a documentação
cargo doc --open

# 4. Publicar
cargo publish
```

---

## Erros Comuns no Portfólio GitHub

1. **Repositórios sem README**: um projeto sem documentação é quase invisível
2. **CI quebrado**: badge vermelha de CI é pior que não ter badge
3. **Código com `unwrap()` em toda parte**: demonstra descuido com tratamento de erros
4. **Sem testes**: código sem testes levanta bandeiras vermelhas
5. **Commits tipo "fix", "update", "asdf"**: histórico de commits ilegível
6. **Muitos forks sem modificação**: polui o perfil sem agregar valor
7. **Projetos todos iguais**: diversifique (CLI, web, lib, contribuição)
8. **Ignorar warnings do Clippy**: ative `-Dwarnings` no CI
9. **Dependências desatualizadas**: mostre que mantém o projeto saudável
10. **Não ter licença**: sem licença, ninguém pode usar seu código legalmente

---

## Conclusão

Seu portfólio no GitHub é a prova concreta das suas habilidades. Para construir um perfil que impressiona:

- **Qualidade sobre quantidade**: 3 projetos excelentes valem mais que 20 mediocres
- **Código idiomático**: siga as convenções da comunidade Rust
- **Documentação completa**: README, doc comments, exemplos
- **CI/CD configurado**: demonstre profissionalismo com automação
- **Contribuições open source**: mostre que sabe colaborar
- **Profile README otimizado**: crie uma boa primeira impressão
- **Consistência**: mantenha atividade regular e projetos atualizados

Invista tempo em polir seus melhores projetos. Cada repositório pinado é uma oportunidade de mostrar ao mundo o que você sabe fazer com Rust.
