---
title: "Portfolio de Desenvolvedor Kotlin: Como Montar o Seu | Kotlin Brasil"
url: "https://kotlin.dev.br/blog/portfolio-desenvolvedor-kotlin/"
markdown_url: "https://kotlin.dev.br/blog/portfolio-desenvolvedor-kotlin.MD"
description: "Como montar um portfólio de desenvolvedor Kotlin que impressiona recrutadores. Projetos, GitHub e dicas práticas para se destacar."
date: "2025-07-27"
author: "Karina Melo"
---

# Portfolio de Desenvolvedor Kotlin: Como Montar o Seu | Kotlin Brasil

Como montar um portfólio de desenvolvedor Kotlin que impressiona recrutadores. Projetos, GitHub e dicas práticas para se destacar.


Um portfólio bem construído pode ser o diferencial entre receber uma proposta de emprego ou ser ignorado. Para desenvolvedores Kotlin, ter projetos que demonstram suas habilidades de forma clara e organizada é fundamental. Neste guia, vou te mostrar exatamente como montar um portfólio que se destaca.

## Por Que Portfólio Importa Mais Que Currículo

No mundo de desenvolvimento, recrutadores e tech leads querem ver código. Um currículo diz o que você afirma saber. Um portfólio prova o que você realmente sabe fazer.

Empresas como Nubank, iFood e PicPay frequentemente pedem acesso ao GitHub do candidato antes mesmo de agendar uma entrevista. Ter projetos bem organizados lá pode abrir portas que um currículo sozinho não abre.

## Estrutura de um Portfólio Eficaz

Um portfólio de desenvolvedor Kotlin deve ter entre 3 e 5 projetos de qualidade. Qualidade importa muito mais que quantidade. Um único projeto bem feito impressiona mais que dez projetos meia-boca.

### Projeto 1: App Android Completo

Este é o projeto obrigatório para quem quer vagas Android. Deve demonstrar:

```kotlin
// Arquitetura limpa com MVVM
// domain/usecase/GetProductsUseCase.kt
class GetProductsUseCase(
    private val repository: ProductRepository
) {
    operator fun invoke(
        category: String? = null,
        sortBy: SortOption = SortOption.NAME
    ): Flow<Result<List<Product>>> {
        return repository.getProducts()
            .map { result ->
                result.map { products ->
                    products
                        .let { list ->
                            if (category != null) {
                                list.filter { it.category == category }
                            } else list
                        }
                        .sortedWith(sortBy.comparator)
                }
            }
    }
}

// presentation/viewmodel/ProductListViewModel.kt
@HiltViewModel
class ProductListViewModel @Inject constructor(
    private val getProducts: GetProductsUseCase,
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    private val _uiState = MutableStateFlow(ProductListUiState())
    val uiState: StateFlow<ProductListUiState> = _uiState.asStateFlow()

    private val selectedCategory = savedStateHandle.getStateFlow<String?>(
        "category", null
    )

    init {
        viewModelScope.launch {
            selectedCategory.flatMapLatest { category ->
                getProducts(category = category)
            }.collect { result ->
                result
                    .onSuccess { products ->
                        _uiState.update {
                            it.copy(products = products, loading = false)
                        }
                    }
                    .onFailure { error ->
                        _uiState.update {
                            it.copy(error = error.message, loading = false)
                        }
                    }
            }
        }
    }
}
```

O app deve consumir uma API pública (como PokeAPI, TMDB ou OpenWeather), ter persistência local, tratamento de erros e uma UI bem construída com Jetpack Compose.

### Projeto 2: API Backend com Kotlin

Mostra que você vai além do mobile:

```kotlin
// API REST com Spring Boot
// Estrutura bem organizada
@RestController
@RequestMapping("/api/v1/bookmarks")
class BookmarkController(
    private val bookmarkService: BookmarkService
) {
    @GetMapping
    suspend fun list(
        @AuthenticationPrincipal user: UserPrincipal,
        @RequestParam(defaultValue = "0") page: Int,
        @RequestParam(defaultValue = "20") size: Int
    ): ResponseEntity<PagedResponse<BookmarkDTO>> {
        val bookmarks = bookmarkService.listByUser(user.id, page, size)
        return ResponseEntity.ok(bookmarks)
    }

    @PostMapping
    suspend fun create(
        @AuthenticationPrincipal user: UserPrincipal,
        @Valid @RequestBody request: CreateBookmarkRequest
    ): ResponseEntity<BookmarkDTO> {
        val bookmark = bookmarkService.create(user.id, request)
        return ResponseEntity.status(HttpStatus.CREATED).body(bookmark)
    }

    @DeleteMapping("/{id}")
    suspend fun delete(
        @AuthenticationPrincipal user: UserPrincipal,
        @PathVariable id: String
    ): ResponseEntity<Unit> {
        bookmarkService.delete(user.id, id)
        return ResponseEntity.noContent().build()
    }
}

// Testes - Fundamental para impressionar
@SpringBootTest
@AutoConfigureMockMvc
class BookmarkControllerTest {
    @Autowired
    private lateinit var mockMvc: MockMvc

    @MockkBean
    private lateinit var bookmarkService: BookmarkService

    @Test
    fun `should return paginated bookmarks`() {
        val bookmarks = listOf(
            createBookmarkDTO(id = "1", title = "Kotlin Blog"),
            createBookmarkDTO(id = "2", title = "Spring Docs")
        )
        coEvery { bookmarkService.listByUser(any(), any(), any()) } returns
            PagedResponse(bookmarks, total = 2, page = 0, size = 20)

        mockMvc.get("/api/v1/bookmarks") {
            header("Authorization", "Bearer ${generateTestToken()}")
        }.andExpect {
            status { isOk() }
            jsonPath("$.content") { isArray() }
            jsonPath("$.content.length()") { value(2) }
            jsonPath("$.total") { value(2) }
        }
    }
}
```

### Projeto 3: Biblioteca ou Ferramenta

Criar uma biblioteca ou CLI mostra maturidade técnica:

```kotlin
// Exemplo: DSL para validação
// Mostra conhecimento avançado de Kotlin
class ValidationBuilder<T> {
    private val rules = mutableListOf<ValidationRule<T>>()

    fun field(
        name: String,
        extractor: (T) -> Any?,
        block: FieldValidationBuilder.() -> Unit
    ) {
        val fieldBuilder = FieldValidationBuilder(name, extractor)
        fieldBuilder.block()
        rules.addAll(fieldBuilder.build())
    }

    fun validate(obj: T): ValidationResult {
        val errors = rules.mapNotNull { rule ->
            rule.validate(obj)
        }
        return if (errors.isEmpty()) {
            ValidationResult.Valid
        } else {
            ValidationResult.Invalid(errors)
        }
    }
}

// Uso da DSL
val userValidator = validator<User> {
    field("nome", { it.nome }) {
        notBlank()
        minLength(2)
        maxLength(100)
    }
    field("email", { it.email }) {
        notBlank()
        matchesPattern(EMAIL_REGEX)
    }
    field("idade", { it.idade }) {
        min(18)
        max(120)
    }
}

val resultado = userValidator.validate(user)
when (resultado) {
    is ValidationResult.Valid -> salvarUsuario(user)
    is ValidationResult.Invalid -> mostrarErros(resultado.errors)
}
```

## Qualidade do Código

Recrutadores olham não apenas se o código funciona, mas como ele é escrito. Siga estas práticas:

### Nomenclatura Clara

```kotlin
// Ruim
fun proc(d: List<Int>): Int = d.filter { it > 0 }.sum()

// Bom
fun calcularSomaPositivos(valores: List<Int>): Int {
    return valores
        .filter { it > 0 }
        .sum()
}
```

### Commit Messages Significativas

Seus commits contam uma historia. Em vez de "fix bug" ou "update code", escreva:
- "Adiciona paginação na listagem de produtos"
- "Corrige tratamento de erro quando API retorna 404"
- "Refatora ViewModel para usar StateFlow em vez de LiveData"

### README Bem Escrito

Cada projeto deve ter um README que explica:
- O que o projeto faz
- Tecnologias utilizadas
- Como rodar localmente
- Screenshots ou GIFs (para apps visuais)
- Decisões arquiteturais e por que foram tomadas

## Organização do GitHub

### Perfil Profissional

Configure seu perfil do GitHub com:
- Foto profissional
- Bio clara mencionando Kotlin
- Link para LinkedIn
- Projetos pinados (os melhores no topo)

### Contribuições Regulares

O gráfico de contribuições do GitHub mostra consistência. Tente:
- Fazer commits regularmente (mesmo pequenos)
- Abrir issues e PRs em projetos open source
- Revisar código de outros

## Testes: O Diferencial

A maioria dos portfólios não tem testes. Ter testes bem escritos é um diferencial enorme:

```kotlin
// Testes de ViewModel com coroutines
class ProductViewModelTest {
    @get:Rule
    val mainDispatcherRule = MainDispatcherRule()

    private val repository = FakeProductRepository()
    private lateinit var viewModel: ProductViewModel

    @Before
    fun setup() {
        viewModel = ProductViewModel(GetProductsUseCase(repository))
    }

    @Test
    fun `loading state should be true initially`() = runTest {
        val states = mutableListOf<ProductListUiState>()
        val job = launch {
            viewModel.uiState.toList(states)
        }

        advanceUntilIdle()

        assertThat(states.first().loading).isTrue()
        assertThat(states.last().loading).isFalse()
        assertThat(states.last().products).isNotEmpty()

        job.cancel()
    }
}
```

## Erros Comuns a Evitar

1. **Projetos incompletos:** Melhor ter 3 projetos terminados do que 10 pela metade
2. **Código copiado de tutoriais:** Recrutadores percebem. Adicione suas proprias features
3. **Sem tratamento de erros:** Apps que crasham causam pessima impressao
4. **Sem .gitignore:** Nunca commite arquivos de build, IDE ou credenciais
5. **Falta de documentação:** Código sem README parece abandonado

## Cronograma Sugerido

Se você está começando do zero, siga este cronograma:

- **Semanas 1-3:** Projeto Android completo
- **Semanas 4-5:** Projeto backend com API
- **Semana 6:** Projeto biblioteca/ferramenta
- **Semana 7:** Polimento, testes e documentação
- **Semana 8:** Revisão final e início das candidaturas

## Conclusão

Montar um portfólio sólido requer dedicação, mas o retorno é enorme. Projetos bem feitos demonstram suas habilidades de forma que nenhum currículo consegue. Foque em qualidade, mantenha o código organizado, escreva testes e documente suas decisões. Para quem está começando, esse cuidado também influencia a faixa inicial de [salário como desenvolvedor Kotlin júnior](/carreira/salario-dev-kotlin-junior/), especialmente em vagas Android e backend com mentoria.

Lembre-se: o portfólio é um documento vivo. Continue melhorando seus projetos, adicionando features e aprendendo novas tecnologias. Ter projetos em múltiplas linguagens como <a href="https://golang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go</a> ou <a href="https://python.dev.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'python.dev.br' })">Python</a> demonstra versatilidade e amplia suas oportunidades. Cada melhoria é um investimento na sua carreira como desenvolvedor Kotlin.
