A manhã não começa com café, mas com um Merge Request
\n\n7:45 da manhã. O telefone vibra — não é o despertador, é o GitLab que enviou uma notificação: seu Merge Request de ontem passou no pipeline CI/CD. Enquanto a chaleira ferve, abro o notebook e vejo a marca de verificação verde. Primeira vitória do dia. Bom dia, Kotlin.
\n\nMeu nome é Alexey, sou desenvolvedor Kotlin em uma empresa de produtos. Nos últimos três anos, tenho programado em Kotlin para Android e um pouco no backend com Ktor. Se você pensa que o trabalho de um programador é um interminável "Hello World", hoje vou desmistificar isso. Bem-vindo ao meu dia de trabalho real.
\n\n8:30 — Daily Standup: sincronização e contexto
\n\nA primeira coisa que acontece é a daily standup. Nos reunimos no Zoom, mas sem os "ontem-hoje-problemas" desnecessários. Somos concisos: o que foi feito, o que está planejado, se há bloqueios. Hoje estou trabalhando na funcionalidade de Notificações Push para os usuários.
\n\n— Ontem terminei a refatoração do NotificationService, reescrevi com coroutines. Hoje vou conectar o Firebase Cloud Messaging e testar o Flow. Sem bloqueios.
A propósito, é nas daily standups que muitas vezes nascem as melhores ideias de arquitetura. Por exemplo, na semana passada, um colega sugeriu substituir o RxJava pelo Kotlin Flow — e reduzimos a quantidade de código boilerplate em 40%. Veja como ficou agora:
\n\n// Em vez do antigo RxJava:\n// Observable.just(data).subscribeOn(Schedulers.io())\n\n// Usamos Kotlin Flow:\nfun observeNotifications(): Flow<Notification> = flow {\n val service = NotificationService()\n while (true) {\n emit(service.getNextNotification())\n delay(5000) // polling a cada 5 segundos\n }\n}.flowOn(Dispatchers.IO)\n\nA diferença é óbvia: legibilidade, segurança de tipos e nada de callback hell.
\n\n9:15 — Desenvolvimento: escrevendo código em Kotlin
\n\nApós a daily standup, mergulho no código. Hoje a tarefa é implementar o processamento de notificações push recebidas. Uso Kotlin Coroutines e StateFlow para gerenciar o estado da UI. Veja como está minha ViewModel:
\n\nclass PushViewModel(\n private val repository: NotificationRepository\n) : ViewModel() {\n\n private val _uiState = MutableStateFlow<PushUiState>(PushUiState.Loading)\n val uiState: StateFlow<PushUiState> = _uiState.asStateFlow()\n\n fun loadNotifications() {\n viewModelScope.launch {\n repository.getNotifications()\n .catch { e ->\n _uiState.value = PushUiState.Error(e.message ?: "Erro desconhecido")\n }\n .collect { notifications ->\n _uiState.value = PushUiState.Success(notifications)\n }\n }\n }\n}\n\nsealed class PushUiState {\n object Loading : PushUiState()\n data class Success(val data: List<Notification>) : PushUiState()\n data class Error(val message: String) : PushUiState()\n}\n\nObserve: estou usando sealed class para o estado da UI. Essa é uma prática padrão no desenvolvimento Android moderno com Kotlin. Nada de cadeias if-else — apenas tipagem pura.
\n\nA propósito, se você é iniciante em Kotlin, recomendo prestar atenção nas Scope functions: let, apply, also. Elas tornam o código mais conciso. Por exemplo:
// Sem scope functions\nval user = User()\nuser.name = "Alex"\nuser.age = 30\nsaveUser(user)\n\n// Com apply\nval user = User().apply {\n name = "Alex"\n age = 30\n}\nsaveUser(user)\n\n11:00 — Code Review: aprendendo com os colegas
\n\nApós o almoço (sim, almoço às 11 — um hábito do trabalho remoto) abro um Merge Request de um colega. Ele está reescrevendo o módulo de autenticação com Kotlin Multiplatform. Leio o código e vejo um ponto interessante:
\n\n// Era (