Plano de Estudos Rust: Nível Pleno (6-18 Meses)

Roadmap para evoluir de júnior para pleno em Rust. Async/await, macros, design patterns, contribuição open source e projetos complexos para consolidar sua carreira.

Você já domina os fundamentos do Rust, tem alguns projetos no portfólio e talvez até já esteja trabalhando profissionalmente com a linguagem. Agora é hora de dar o próximo salto: tornar-se um desenvolvedor pleno capaz de lidar com sistemas complexos, tomar decisões arquiteturais e contribuir significativamente para projetos maiores.

Este plano cobre o período de 6 a 18 meses de experiência com Rust e pressupõe que você já domina ownership, borrowing, traits básicos, tratamento de erros e estruturação de projetos. Se ainda não se sente confortável com esses temas, recomendamos seguir primeiro o nosso Plano de Estudos Júnior.

Visão Geral do Roadmap

PeríodoFocoResultado Esperado
Meses 7-9Generics avançados, async/await, macrosEscrever código sofisticado e performático
Meses 10-12Design patterns, arquitetura, bancos de dadosProjetar sistemas robustos
Meses 13-15Open source, web frameworks avançadosContribuir para o ecossistema
Meses 16-18Projeto complexo + System designDemonstrar capacidade pleno

Meses 7-9: Domínio Técnico Avançado

Generics Avançados e Trait Bounds

Generics no nível pleno vão muito além de fn foo<T>(x: T). Você precisa dominar trait bounds compostos, tipos associados, supertraits e uso avançado de where clauses.

Tópicos para dominar:

  • Trait bounds múltiplos e compostos
  • Tipos associados vs. parâmetros genéricos
  • Supertraits e herança de traits
  • impl Trait em posição de argumento e retorno
  • Trait objects (dyn Trait) e object safety
  • Phantom types e marker traits
use std::fmt;
use std::ops::Add;

// Trait com tipo associado
trait Medida {
    type Unidade: fmt::Display;

    fn valor(&self) -> f64;
    fn unidade(&self) -> Self::Unidade;
    fn formatar(&self) -> String {
        format!("{:.2} {}", self.valor(), self.unidade())
    }
}

// Supertrait: Combinavel requer Medida + Clone
trait Combinavel: Medida + Clone {
    fn combinar(&self, outro: &Self) -> Self;
}

// Generics com where clause complexa
fn comparar_e_somar<T, U>(a: &T, b: &U) -> String
where
    T: Medida + fmt::Debug,
    U: Medida + fmt::Debug,
    T::Unidade: PartialEq<U::Unidade>,
{
    format!(
        "{} vs {} (total: {:.2})",
        a.formatar(),
        b.formatar(),
        a.valor() + b.valor()
    )
}

// Phantom types para segurança de tipos
use std::marker::PhantomData;

struct Validado;
struct NaoValidado;

struct Email<Estado = NaoValidado> {
    endereco: String,
    _estado: PhantomData<Estado>,
}

impl Email<NaoValidado> {
    fn novo(endereco: String) -> Self {
        Email {
            endereco,
            _estado: PhantomData,
        }
    }

    fn validar(self) -> Result<Email<Validado>, String> {
        if self.endereco.contains('@') && self.endereco.contains('.') {
            Ok(Email {
                endereco: self.endereco,
                _estado: PhantomData,
            })
        } else {
            Err("Email inválido".to_string())
        }
    }
}

impl Email<Validado> {
    fn enviar(&self, mensagem: &str) {
        println!("Enviando '{}' para {}", mensagem, self.endereco);
    }
}

Async/Await e Programação Assíncrona

Programação assíncrona é essencial para qualquer desenvolvedor Rust pleno, especialmente para trabalho com web, I/O e sistemas distribuídos.

Tópicos para dominar:

  • Como futures funcionam internamente
  • Tokio runtime (single-threaded vs. multi-threaded)
  • Channels (mpsc, broadcast, watch, oneshot)
  • Select! e combinadores de futures
  • Streams e AsyncRead/AsyncWrite
  • Padrões comuns: fan-out/fan-in, rate limiting, timeouts
  • Pin e Unpin (conceitual)
use tokio::sync::mpsc;
use tokio::time::{self, Duration};
use std::collections::HashMap;

// Worker pool assíncrono
struct WorkerPool {
    sender: mpsc::Sender<Tarefa>,
}

struct Tarefa {
    id: u64,
    url: String,
    resposta: tokio::sync::oneshot::Sender<Resultado>,
}

struct Resultado {
    id: u64,
    status: u16,
    tempo_ms: u64,
}

impl WorkerPool {
    fn novo(num_workers: usize) -> Self {
        let (sender, receiver) = mpsc::channel::<Tarefa>(100);
        let receiver = std::sync::Arc::new(tokio::sync::Mutex::new(receiver));

        for i in 0..num_workers {
            let rx = receiver.clone();
            tokio::spawn(async move {
                loop {
                    let tarefa = {
                        let mut guard = rx.lock().await;
                        guard.recv().await
                    };

                    match tarefa {
                        Some(t) => {
                            let inicio = std::time::Instant::now();
                            // Simula processamento HTTP
                            time::sleep(Duration::from_millis(100)).await;
                            let resultado = Resultado {
                                id: t.id,
                                status: 200,
                                tempo_ms: inicio.elapsed().as_millis() as u64,
                            };
                            let _ = t.resposta.send(resultado);
                            println!("Worker {} processou tarefa {}", i, t.id);
                        }
                        None => break,
                    }
                }
            });
        }

        WorkerPool { sender }
    }

    async fn enviar(&self, id: u64, url: String) -> Resultado {
        let (tx, rx) = tokio::sync::oneshot::channel();
        let tarefa = Tarefa {
            id,
            url,
            resposta: tx,
        };
        self.sender.send(tarefa).await.unwrap();
        rx.await.unwrap()
    }
}

// Padrão: timeout com fallback
async fn buscar_com_timeout(url: &str, timeout_secs: u64) -> Result<String, String> {
    match time::timeout(
        Duration::from_secs(timeout_secs),
        buscar_dados(url),
    )
    .await
    {
        Ok(Ok(dados)) => Ok(dados),
        Ok(Err(e)) => Err(format!("Erro na requisição: {}", e)),
        Err(_) => Err(format!("Timeout após {}s", timeout_secs)),
    }
}

async fn buscar_dados(url: &str) -> Result<String, String> {
    // Simulação de requisição HTTP
    time::sleep(Duration::from_millis(50)).await;
    Ok(format!("Dados de {}", url))
}

Macros em Rust

Macros são uma ferramenta poderosa para reduzir boilerplate e criar DSLs. No nível pleno, você precisa saber quando e como usá-las.

Macros declarativas (macro_rules!):

// Macro para criar um HashMap facilmente
macro_rules! mapa {
    ($($chave:expr => $valor:expr),* $(,)?) => {
        {
            let mut map = std::collections::HashMap::new();
            $(map.insert($chave, $valor);)*
            map
        }
    };
}

// Macro para criar builders automaticamente
macro_rules! builder {
    ($nome:ident { $($campo:ident: $tipo:ty),* $(,)? }) => {
        pub struct $nome {
            $($campo: Option<$tipo>,)*
        }

        impl $nome {
            pub fn novo() -> Self {
                $nome {
                    $($campo: None,)*
                }
            }

            $(
                pub fn $campo(mut self, valor: $tipo) -> Self {
                    self.$campo = Some(valor);
                    self
                }
            )*
        }
    };
}

builder!(ConfigBuilder {
    host: String,
    porta: u16,
    max_conexoes: usize,
    timeout_ms: u64,
});

fn main() {
    let config = mapa! {
        "ambiente" => "produção",
        "versão" => "1.0",
        "região" => "br-south",
    };

    let cfg = ConfigBuilder::novo()
        .host("localhost".to_string())
        .porta(8080)
        .max_conexoes(100)
        .timeout_ms(5000);

    println!("Config criada com {} entradas", config.len());
}

Macros procedurais (visão geral):

// Exemplo conceitual de derive macro
// Este código vai em uma crate separada do tipo proc-macro

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(Descritivel)]
pub fn derive_descritivel(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let nome = &input.ident;

    let expanded = quote! {
        impl Descritivel for #nome {
            fn descricao(&self) -> String {
                format!("Instância de {}", stringify!(#nome))
            }
        }
    };

    TokenStream::from(expanded)
}

Checklist dos Meses 7-9

  • Usa generics avançados com trait bounds compostos
  • Entende e usa tipos associados e supertraits
  • Escreve código assíncrono com Tokio fluentemente
  • Sabe usar channels, select e timeouts
  • Cria macros declarativas para eliminar boilerplate
  • Entende conceitualmente como macros procedurais funcionam

Meses 10-12: Design Patterns e Arquitetura

Design Patterns em Rust

Rust tem sua própria abordagem para design patterns clássicos. Muitos patterns de OOP não se aplicam diretamente, e Rust introduz patterns únicos.

Padrões essenciais:

// 1. Builder Pattern (muito comum em Rust)
pub struct HttpRequest {
    url: String,
    metodo: String,
    headers: Vec<(String, String)>,
    body: Option<String>,
    timeout: std::time::Duration,
}

pub struct HttpRequestBuilder {
    url: String,
    metodo: String,
    headers: Vec<(String, String)>,
    body: Option<String>,
    timeout: std::time::Duration,
}

impl HttpRequestBuilder {
    pub fn new(url: impl Into<String>) -> Self {
        HttpRequestBuilder {
            url: url.into(),
            metodo: "GET".to_string(),
            headers: Vec::new(),
            body: None,
            timeout: std::time::Duration::from_secs(30),
        }
    }

    pub fn metodo(mut self, metodo: impl Into<String>) -> Self {
        self.metodo = metodo.into();
        self
    }

    pub fn header(mut self, chave: impl Into<String>, valor: impl Into<String>) -> Self {
        self.headers.push((chave.into(), valor.into()));
        self
    }

    pub fn body(mut self, body: impl Into<String>) -> Self {
        self.body = Some(body.into());
        self
    }

    pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
        self.timeout = timeout;
        self
    }

    pub fn build(self) -> HttpRequest {
        HttpRequest {
            url: self.url,
            metodo: self.metodo,
            headers: self.headers,
            body: self.body,
            timeout: self.timeout,
        }
    }
}

// 2. Newtype Pattern (para type safety)
struct UserId(u64);
struct OrderId(u64);

// Impossível confundir os dois acidentalmente!
fn buscar_pedido(user_id: UserId, order_id: OrderId) {
    println!("Buscando pedido {} do usuário {}", order_id.0, user_id.0);
}

// 3. Type State Pattern
struct Rascunho;
struct EmRevisao;
struct Publicado;

struct Artigo<Estado> {
    titulo: String,
    conteudo: String,
    _estado: std::marker::PhantomData<Estado>,
}

impl Artigo<Rascunho> {
    fn novo(titulo: String) -> Self {
        Artigo {
            titulo,
            conteudo: String::new(),
            _estado: std::marker::PhantomData,
        }
    }

    fn escrever(mut self, conteudo: String) -> Self {
        self.conteudo = conteudo;
        self
    }

    fn enviar_para_revisao(self) -> Artigo<EmRevisao> {
        Artigo {
            titulo: self.titulo,
            conteudo: self.conteudo,
            _estado: std::marker::PhantomData,
        }
    }
}

impl Artigo<EmRevisao> {
    fn aprovar(self) -> Artigo<Publicado> {
        Artigo {
            titulo: self.titulo,
            conteudo: self.conteudo,
            _estado: std::marker::PhantomData,
        }
    }

    fn rejeitar(self) -> Artigo<Rascunho> {
        Artigo {
            titulo: self.titulo,
            conteudo: self.conteudo,
            _estado: std::marker::PhantomData,
        }
    }
}

impl Artigo<Publicado> {
    fn url(&self) -> String {
        format!("/artigos/{}", self.titulo.to_lowercase().replace(' ', "-"))
    }
}

Acesso a Bancos de Dados em Profundidade

Tópicos para dominar:

  • Connection pooling com sqlx
  • Migrations e schema management
  • Transactions e consistência
  • Query builders vs. raw SQL
  • ORMs em Rust (diesel, sea-orm)
  • Padrões: Repository, Unit of Work
use sqlx::{postgres::PgPoolOptions, FromRow, Pool, Postgres};

#[derive(Debug, FromRow)]
struct Usuario {
    id: i64,
    nome: String,
    email: String,
    criado_em: chrono::NaiveDateTime,
}

struct UsuarioRepository {
    pool: Pool<Postgres>,
}

impl UsuarioRepository {
    fn new(pool: Pool<Postgres>) -> Self {
        UsuarioRepository { pool }
    }

    async fn criar(&self, nome: &str, email: &str) -> Result<Usuario, sqlx::Error> {
        sqlx::query_as::<_, Usuario>(
            "INSERT INTO usuarios (nome, email) VALUES ($1, $2) RETURNING *"
        )
        .bind(nome)
        .bind(email)
        .fetch_one(&self.pool)
        .await
    }

    async fn buscar_por_id(&self, id: i64) -> Result<Option<Usuario>, sqlx::Error> {
        sqlx::query_as::<_, Usuario>("SELECT * FROM usuarios WHERE id = $1")
            .bind(id)
            .fetch_optional(&self.pool)
            .await
    }

    async fn listar_paginado(
        &self,
        pagina: i64,
        por_pagina: i64,
    ) -> Result<Vec<Usuario>, sqlx::Error> {
        let offset = (pagina - 1) * por_pagina;
        sqlx::query_as::<_, Usuario>(
            "SELECT * FROM usuarios ORDER BY criado_em DESC LIMIT $1 OFFSET $2"
        )
        .bind(por_pagina)
        .bind(offset)
        .fetch_all(&self.pool)
        .await
    }

    async fn transferir_creditos(
        &self,
        de_id: i64,
        para_id: i64,
        valor: f64,
    ) -> Result<(), sqlx::Error> {
        let mut tx = self.pool.begin().await?;

        sqlx::query("UPDATE contas SET saldo = saldo - $1 WHERE usuario_id = $2 AND saldo >= $1")
            .bind(valor)
            .bind(de_id)
            .execute(&mut *tx)
            .await?;

        sqlx::query("UPDATE contas SET saldo = saldo + $1 WHERE usuario_id = $2")
            .bind(valor)
            .bind(para_id)
            .execute(&mut *tx)
            .await?;

        tx.commit().await?;
        Ok(())
    }
}

Web Frameworks em Profundidade

Aprofunde-se no framework web de sua escolha (Axum recomendado):

use axum::{
    extract::{Path, Query, State},
    http::StatusCode,
    middleware,
    response::IntoResponse,
    routing::{get, post, put, delete},
    Json, Router,
};
use serde::{Deserialize, Serialize};
use tower_http::{cors::CorsLayer, trace::TraceLayer};

// Middleware customizado para autenticação
async fn auth_middleware(
    req: axum::extract::Request,
    next: axum::middleware::Next,
) -> Result<impl IntoResponse, StatusCode> {
    let auth_header = req
        .headers()
        .get("Authorization")
        .and_then(|v| v.to_str().ok());

    match auth_header {
        Some(token) if token.starts_with("Bearer ") => {
            // Validar token aqui
            Ok(next.run(req).await)
        }
        _ => Err(StatusCode::UNAUTHORIZED),
    }
}

// Extrator customizado para paginação
#[derive(Deserialize)]
struct Paginacao {
    #[serde(default = "pagina_padrao")]
    pagina: u32,
    #[serde(default = "limite_padrao")]
    limite: u32,
}

fn pagina_padrao() -> u32 { 1 }
fn limite_padrao() -> u32 { 20 }

// Resposta padronizada
#[derive(Serialize)]
struct RespostaPaginada<T: Serialize> {
    dados: Vec<T>,
    pagina: u32,
    total_paginas: u32,
    total_itens: u64,
}

// Tratamento de erros centralizado
enum AppError {
    NaoEncontrado(String),
    Validacao(String),
    Interno(String),
}

impl IntoResponse for AppError {
    fn into_response(self) -> axum::response::Response {
        let (status, mensagem) = match self {
            AppError::NaoEncontrado(msg) => (StatusCode::NOT_FOUND, msg),
            AppError::Validacao(msg) => (StatusCode::BAD_REQUEST, msg),
            AppError::Interno(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg),
        };

        let body = serde_json::json!({
            "erro": mensagem,
            "status": status.as_u16(),
        });

        (status, Json(body)).into_response()
    }
}

fn criar_router(estado: AppState) -> Router {
    let rotas_publicas = Router::new()
        .route("/health", get(|| async { "OK" }))
        .route("/login", post(login));

    let rotas_protegidas = Router::new()
        .route("/usuarios", get(listar_usuarios))
        .route("/usuarios/:id", get(buscar_usuario))
        .route("/usuarios", post(criar_usuario))
        .route("/usuarios/:id", put(atualizar_usuario))
        .route("/usuarios/:id", delete(deletar_usuario))
        .layer(middleware::from_fn(auth_middleware));

    Router::new()
        .merge(rotas_publicas)
        .merge(rotas_protegidas)
        .layer(CorsLayer::permissive())
        .layer(TraceLayer::new_for_http())
        .with_state(estado)
}

Princípios de System Design

Conceitos que um desenvolvedor pleno deve dominar:

  1. Separação de responsabilidades: organize código em camadas (handler, service, repository)
  2. Injeção de dependências: use traits para abstrair dependências e facilitar testes
  3. Configuração externalizada: use variáveis de ambiente e arquivos de configuração
  4. Logging estruturado: use tracing para logs com contexto
  5. Health checks e métricas: exponha endpoints de monitoramento
  6. Graceful shutdown: trate sinais de desligamento corretamente
// Exemplo de arquitetura em camadas com injeção de dependência

// Camada de abstração (trait)
#[async_trait::async_trait]
trait ProdutoRepository: Send + Sync {
    async fn buscar(&self, id: i64) -> Result<Option<Produto>, AppError>;
    async fn listar(&self, filtro: FiltroProduto) -> Result<Vec<Produto>, AppError>;
    async fn criar(&self, produto: NovoProduto) -> Result<Produto, AppError>;
}

// Camada de serviço (lógica de negócio)
struct ProdutoService<R: ProdutoRepository> {
    repo: R,
}

impl<R: ProdutoRepository> ProdutoService<R> {
    fn new(repo: R) -> Self {
        ProdutoService { repo }
    }

    async fn criar_produto(&self, input: NovoProduto) -> Result<Produto, AppError> {
        // Validação de regras de negócio
        if input.preco <= 0.0 {
            return Err(AppError::Validacao("Preço deve ser positivo".into()));
        }
        if input.nome.len() < 3 {
            return Err(AppError::Validacao("Nome muito curto".into()));
        }

        self.repo.criar(input).await
    }
}

// Implementação real (produção)
struct PgProdutoRepository {
    pool: sqlx::PgPool,
}

// Implementação fake (testes)
struct FakeProdutoRepository {
    produtos: std::sync::Mutex<Vec<Produto>>,
}

Checklist dos Meses 10-12

  • Conhece e aplica os principais design patterns em Rust
  • Trabalha com bancos de dados usando transactions e migrations
  • Constrói APIs completas com autenticação e tratamento de erros
  • Entende arquitetura em camadas e injeção de dependências
  • Usa logging estruturado com tracing
  • Sabe projetar sistemas com separação clara de responsabilidades

Meses 13-15: Contribuição Open Source e Profundidade

Contribuindo para Projetos Open Source

Contribuir para projetos open source é uma das melhores formas de acelerar seu crescimento como desenvolvedor Rust.

Como começar:

  1. Escolha projetos que você usa: é mais fácil contribuir para algo que você conhece como usuário
  2. Comece por issues rotuladas como “good first issue” ou “help wanted”
  3. Leia o CONTRIBUTING.md antes de qualquer coisa
  4. Comece com documentação e testes: contribuições de docs são muito valorizadas
  5. Revisões de código: acompanhe PRs de outros para aprender

Projetos recomendados para primeiras contribuições:

ProjetoÁreaDificuldade
RustlingsEducaçãoBaixa
mdBookFerramentasBaixa-Média
TokioAsync runtimeMédia
AxumWeb frameworkMédia
SerdeSerializaçãoMédia-Alta
Rust ClippyLintingMédia

Fluxo de contribuição:

# 1. Fork e clone
git clone https://github.com/seu-usuario/projeto-fork.git
cd projeto-fork
git remote add upstream https://github.com/projeto-original/projeto.git

# 2. Crie uma branch para sua contribuição
git checkout -b fix/corrigir-typo-docs

# 3. Faça as alterações, teste localmente
cargo test
cargo clippy
cargo fmt --check

# 4. Commit e push
git add .
git commit -m "fix: corrige typo na documentação do módulo X"
git push origin fix/corrigir-typo-docs

# 5. Abra um Pull Request no GitHub

Tópicos Avançados de Web

Aprofunde-se em:

  • WebSockets e Server-Sent Events
  • GraphQL com async-graphql
  • gRPC com tonic
  • Rate limiting e circuit breaker
  • Caching (Redis com deadpool-redis)
  • Background jobs e filas
  • Autenticação JWT e OAuth2
// Exemplo: WebSocket com Axum
use axum::{
    extract::ws::{Message, WebSocket, WebSocketUpgrade},
    response::IntoResponse,
    routing::get,
    Router,
};
use futures::{SinkExt, StreamExt};

async fn ws_handler(ws: WebSocketUpgrade) -> impl IntoResponse {
    ws.on_upgrade(handle_socket)
}

async fn handle_socket(mut socket: WebSocket) {
    // Enviar mensagem de boas-vindas
    if socket
        .send(Message::Text("Bem-vindo ao chat!".to_string()))
        .await
        .is_err()
    {
        return;
    }

    // Loop de recebimento
    while let Some(Ok(msg)) = socket.next().await {
        match msg {
            Message::Text(texto) => {
                println!("Recebido: {}", texto);
                let resposta = format!("Echo: {}", texto);
                if socket.send(Message::Text(resposta)).await.is_err() {
                    break;
                }
            }
            Message::Close(_) => break,
            _ => {}
        }
    }
}

Ferramentas e Ecossistema

Domine as ferramentas que diferenciam um desenvolvedor pleno:

  • cargo-watch: recompilação automática durante desenvolvimento
  • cargo-expand: visualizar macros expandidas
  • cargo-audit: verificar vulnerabilidades em dependências
  • cargo-deny: políticas de licença e segurança
  • cargo-flamegraph: profiling de performance
  • cargo-tarpaulin: cobertura de testes
# Instalar ferramentas essenciais
cargo install cargo-watch cargo-expand cargo-audit cargo-deny

# Desenvolvimento com hot-reload
cargo watch -x "run" -w src/

# Verificar segurança das dependências
cargo audit

# Expandir macros para debug
cargo expand nome_do_modulo

# Cobertura de testes
cargo install cargo-tarpaulin
cargo tarpaulin --out html

Checklist dos Meses 13-15

  • Tem pelo menos 1 PR aceito em projeto open source
  • Sabe trabalhar com WebSockets ou gRPC
  • Implementa caching e background jobs
  • Domina as ferramentas do ecossistema Cargo
  • Faz code review de PRs de outros desenvolvedores

Meses 16-18: Projeto Complexo e Consolidação

Projeto: Sistema Distribuído ou Aplicação Complexa

Este é o projeto que vai consolidar seu nível pleno. Escolha uma das opções abaixo:

Opção A: Sistema de Mensageria

  • Broker de mensagens com publish/subscribe
  • Múltiplos consumidores com grupos
  • Persistência de mensagens
  • Dashboard de monitoramento
  • Protocolo customizado sobre TCP

Opção B: Motor de Busca Simplificado

  • Indexação de documentos (TF-IDF)
  • Busca com ranking de relevância
  • API REST para ingestão e consulta
  • Processamento concorrente de documentos
  • Persistência do índice em disco

Opção C: Plataforma de CI/CD Simplificada

  • Execução de pipelines definidos em YAML
  • Isolamento de jobs
  • Dashboard web com status em tempo real
  • Notificações (webhook)
  • Armazenamento de artefatos

Estrutura Sugerida do Projeto

meu-sistema/
├── Cargo.toml           # workspace
├── crates/
│   ├── core/            # lógica de negócio
│   │   ├── Cargo.toml
│   │   └── src/
│   ├── api/             # HTTP API
│   │   ├── Cargo.toml
│   │   └── src/
│   ├── worker/          # processamento em background
│   │   ├── Cargo.toml
│   │   └── src/
│   └── common/          # tipos e utilitários compartilhados
│       ├── Cargo.toml
│       └── src/
├── migrations/          # migrações SQL
├── config/              # arquivos de configuração
├── docker-compose.yml   # ambiente de desenvolvimento
└── README.md

Cronograma do Projeto

SemanaAtividade
1-2Design do sistema, definição de APIs e protocolos
3-4Implementação do core e modelos de dados
5-6API REST e integração com banco de dados
7-8Workers, processamento assíncrono
9-10Testes de integração, observabilidade
11-12Polish, documentação, deploy

System Design: O que o Pleno Deve Saber

Ao projetar sistemas, considere sempre:

  1. Escalabilidade: como o sistema se comporta quando a carga aumenta?
  2. Resiliência: o que acontece quando um componente falha?
  3. Observabilidade: você consegue entender o que o sistema está fazendo?
  4. Segurança: quais são as superfícies de ataque?
  5. Manutenabilidade: outro desenvolvedor consegue entender e modificar o código?

Checklist Final: Pronto para Nível Pleno?

Habilidades Técnicas

  • Generics avançados com trait bounds complexos
  • Programação assíncrona com Tokio (channels, select, timeouts)
  • Macros declarativas para eliminar boilerplate
  • Design patterns idiomáticos de Rust (Builder, Newtype, Type State)
  • Bancos de dados com transactions e migrations
  • APIs REST completas com autenticação e error handling
  • Testes unitários, de integração e end-to-end
  • Logging e observabilidade com tracing

Habilidades de Projeto

  • Arquitetura em camadas com separação de responsabilidades
  • Injeção de dependências via traits
  • CI/CD configurado com testes e linting
  • Documentação técnica clara
  • Gerenciamento de dependências e segurança

Habilidades de Colaboração

  • Contribuição aceita em projeto open source
  • Faz code reviews construtivos
  • Sabe explicar decisões técnicas
  • Mentora desenvolvedores mais iniciantes
  • Participa ativamente da comunidade Rust

Próximos Passos

Após consolidar o nível pleno, você estará preparado para:

  • Liderar projetos técnicos de média complexidade
  • Tomar decisões arquiteturais com segurança
  • Mentorar desenvolvedores júnior
  • Contribuir significativamente para projetos open source
  • Buscar posições sênior ou de liderança técnica

O caminho para sênior envolve aprofundar-se em áreas como unsafe Rust, otimização de performance, arquitetura de larga escala e liderança técnica. Confira nosso Plano de Estudos Sênior para continuar sua jornada.

A chave para o crescimento contínuo é nunca parar de aprender, ensinar e construir. Cada projeto complexo que você finaliza, cada contribuição open source que você faz, cada mentoria que oferece – tudo isso compõe o profissional pleno que o mercado valoriza.

Continue construindo. Continue contribuindo. Continue evoluindo.