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íodo | Foco | Resultado Esperado |
|---|---|---|
| Meses 7-9 | Generics avançados, async/await, macros | Escrever código sofisticado e performático |
| Meses 10-12 | Design patterns, arquitetura, bancos de dados | Projetar sistemas robustos |
| Meses 13-15 | Open source, web frameworks avançados | Contribuir para o ecossistema |
| Meses 16-18 | Projeto complexo + System design | Demonstrar 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 Traitem 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
PineUnpin(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:
- Separação de responsabilidades: organize código em camadas (handler, service, repository)
- Injeção de dependências: use traits para abstrair dependências e facilitar testes
- Configuração externalizada: use variáveis de ambiente e arquivos de configuração
- Logging estruturado: use
tracingpara logs com contexto - Health checks e métricas: exponha endpoints de monitoramento
- 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:
- Escolha projetos que você usa: é mais fácil contribuir para algo que você conhece como usuário
- Comece por issues rotuladas como “good first issue” ou “help wanted”
- Leia o CONTRIBUTING.md antes de qualquer coisa
- Comece com documentação e testes: contribuições de docs são muito valorizadas
- Revisões de código: acompanhe PRs de outros para aprender
Projetos recomendados para primeiras contribuições:
| Projeto | Área | Dificuldade |
|---|---|---|
| Rustlings | Educação | Baixa |
| mdBook | Ferramentas | Baixa-Média |
| Tokio | Async runtime | Média |
| Axum | Web framework | Média |
| Serde | Serialização | Média-Alta |
| Rust Clippy | Linting | Mé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
| Semana | Atividade |
|---|---|
| 1-2 | Design do sistema, definição de APIs e protocolos |
| 3-4 | Implementação do core e modelos de dados |
| 5-6 | API REST e integração com banco de dados |
| 7-8 | Workers, processamento assíncrono |
| 9-10 | Testes de integração, observabilidade |
| 11-12 | Polish, documentação, deploy |
System Design: O que o Pleno Deve Saber
Ao projetar sistemas, considere sempre:
- Escalabilidade: como o sistema se comporta quando a carga aumenta?
- Resiliência: o que acontece quando um componente falha?
- Observabilidade: você consegue entender o que o sistema está fazendo?
- Segurança: quais são as superfícies de ataque?
- 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.