Opa Alberto, Excelente video, obrigado por compartilhar 👏🏻👏🏻 Tem algo (um argumento) que eh sempre confuso nessa discussão: "não use exception para controle de fluxo". Porém, isso está de certa forma incompleta. Na minha opinião, a mensagem correta do argumento deveria ser: "Use exceptions SOMENTE para controle de fluxos DE ERRO". No seu video, vc fez correto, usou exception para controle de fluxo DE ERRO. Incorreto seria usar exceptions para outros tipos de fluxo de controle, como sucesso, repeat-loop, go-to etc. Quanto ao debate de "use somente em casos excepcionais", essa eh outra discussao. 😬 No fim, tenho seu mesmo feeling: favoreça os contructos da linguagem em vez de tentar lutar com eles. Chuto que 99% dos casos será mais simples em diversos niveis do seu código (tente inibir o uso de exceptions com Spring Boot para você ver!).
Complementando... Um bom design de código deixa claro para o client as boundaries para error handling, como a check-exception tenta fazer. O client (caller) está ciente das possibilidades de erro. Porém em termos de experiencia eh MUITO ruim lidar com checked-exceptions (você comentou no seu video). Nao à toa a industria Java há 20 anos adotou UNchecked-exceptions como padrão, nao só no nivel da linguagem, mas tambem frameworks, libs, literatura etc. Outro ponto, é que alguns autores recomendam usar UNchecked-exceptions para comunicação entre modulos de forma mais aberta e ampla, enquanto recomendam checked-exceptions apenas para comunicação em internal-modules. Voce até comentou no video algo do tipo também! Sobre design de código... Conseguimos melhorar o uso de UNchecked-exceptions declarando um `throws` no método e/ou na JavaDoc, mas sabemos que eh bem incomum (a industria não curte muito também hehe). Eh uma forma de compensar o que não temos nas checked-exceptions. Ainda assim, tudo pode vir por agua abaixo se o dev(a) declara uma exception mais generica no `throws` ou esquece de adicionar todos os casos de erro no `throws` (novas exceptions). Isso mata o determinismo do modelo de erros pro client (caller). Sei que vc discorda um pouco aqui. Mas nao ser explicito no `throws` não eh a pior a coisa do mundo em apps corporativas onde o dev tem acesso a todo o codigo fonte e muitas vezes ao autor do codigo. No mundo open-source a discussao eh outra, obviamente.
Enfim, lançar exceptions não eh algo ruim no mundo Java por motivos como: 1. eh proprio da linguagem; 2. a industria abraçou UNchecked-exceptions há quase 20 anos; 3. todo o ecossistema Java segue esse modelo de error handling; 4. as libs e frameworks que não seguem sao exceções a regra e geralmente mudam todo o modo de programar do dev(a); 5. lutar contra isso eh possivel, mas seria doloroso com libs populares como Spring, Hibernate e demais (novamente, tente inibir o uso de exceptions com Spring para ter uma ideia);
Muito bom vídeo Alberto, obrigado por compartilhar. Pensa num sistema grande, grande mesmo, eu trabalho em um hoje enorme, e temos devs de todos os níveis, júnior, pleno, senior, etc... nesse caso eu penso que usar o padrão da linguagem é melhor, pois não é necessário ficar explicando pra ninguém como tratar erros, usa o try/catch e pronto, todos já aprenderam assim. Mas concordo que tem que tomar cuidado, nos code reviews precisamos ficar de olho se ninguém tá querendo controlar um fluxo com exception, mas sim tratar um erro de negócio (como o que você mostrou no vídeo).
Acho que concordo! Digo acho, pq realmente tem esses casos de exceptions como "go to". Mas foi aí que parei e pensei: A culpa não é da exception se ela é usada de maneira estranha hehe
Reflexão muito boa a respeito dos controles de fluxo, mas também em como refletir em padrões tido como "boas práticas" geram novas ideias e podem trazer melhorias para os nossos cenários específicos. Concordo totalmente em utilizar as unchecked exceptions e anotando-as no método. Pode ser que alguns desenvolvedores não sigam a risca, mas aí entra no que foi comentado o problema ta em que tá usando e não na ferramenta. Além do mais se tivermos testes garantindo que tais exceções são tratadas, um dev desavisado vai ter que se esforçar muito mais para quebrar o padrão estabelecido no design do código, reduzindo tais cenários.
Muito boa essa discussão. Vídeo excelente. Na minha visão existem: Exceção de negócio e Exceção de sistema/código Também é possível usar algum mecanismo de Validação como retorno. Eu particularmente não gosto muito de Validação, geralmente dou preferência a Exceção de negócio. Só parto para um framework de Validação quando se torna complexo com diversos retornos. Exceção de negócio não é necessariamente algo totalmente inesperado, um fluxo apenas de erros, tem que ser um fluxo meio que excepcional, pois isso custa demais em tempo de processamento.
Opa, valeu demais pelo comentário. Tem esse lance do processamento mesmo. Ao mesmo tempo, tenho a impressão, aí falando apenas pelas minhas experiências, que para virar um problema precisa ser algo realmente tenso.
@@DevEficientenum sistema em que acabei criando algumas camadas, usei exceptions como controle de fluxo, a exception pulava diversas camadas e caia num tratamento global, a exception foi usada como um evento, realmente não vi muito problema no desempenho. O problema que notei foi que os outros desenvolvedores aparentemente estranhavam um pouco, estavam acostumados a entender a exception como erro de sistema exclusivamente.
Sempre trabalhei com checked exceptions em Java e vejo que a tipagem do Java ajuda demais a tratar os "erros previstos" cada vez que você usar o método que dispara as exceções checadas... Isso dá a oportunidade de quem usa o método, saber as possibilidades previstas.. Hoje trabalho muito com TypeScript, sinto muita falta das checked exceptions que não existem... Trabalho com golang também e tratamento de erros em Go é uma das partes que mais odeio... O padrão da linguagem é esse, mudar isso só se o benefício for muito maior do que a carga cognitiva de usar outro padrão...
Eu sou do time que gosta de checked exceptions tbm, os melhores projetos que trabalhei faziam bastante uso deles. Vejo bastante gente que não gosta, mas vejo tbm gente q sente falta qndo vai pra outros projetos heh
Pra mim a regra é simples, se sua linguagem não suporta Pattern match exaustivo e robusto como Haskell e F# por exemplo não vale a pena usar Result. Se até mesmo as "gambiarras" funcionais em C# e Java fornecidas pela linguagem podem tornar o código confuso algo feito pela comunidade (lib) tem muito mais chances. Finalizando eu gostaria de falar que já tive esse mesmo dilema que tu apresenta no vídeo, a gente geralmente tenta encaixar coisas onde não dá, mas olhando o código a utilização do Result seria uma boa caso você tratasse ele como uma mônada para manter seu código puro, mas isso faria você ter que criar uma classe para identificar cada erro ou fazer uma factory para eles... além disso injetar funções anônimas torna o código menos legível e por isso fica mais confuso que o try catch e se você precisa saber qual o motivo do erro para tratar explicitamente no código, o jeito é ir para as Exceptions e tá tudo bem hshshsh
Hehehe, foi exatamente onde caí. Usei as exceptions para representar os erros e tratei os fluxos com funções... Aí parei e pensei: Poxa, era melhor usar exceptions.
legal, um ponto de vista diferente que complementou o meu entendimento que tinha do tratamento de erro do Clean Code, complementou o que eu ja pensava sobre também.
Lembra das regras de complexidade que você indicava pontos de melhorias, quantidade de if, quantidade de linhas, chegou a fazer um template para sonarqube, meio que seria um lint centralizado das convenções, com um requisito para parar na pipe line de deploy, seria obrigatório os ajustes
(Só assisti o video até os 9 minutos ainda) Mas ai acho que o Java não mostrar que isso é um código morto vai mais do defeito da linguagem, Se vc olha pra Rust por exemplo o retorno de erro é sempre um enum especifco, se vc parar de retornar aquele erro o typesystem vai te avisar que aquela branch não existe mais, o que casa ainda mais com o patten matching que possibilita vc tratar só algumas clausulas do erro.
Sigo algumas regras: 1. Métodos que têm retorno não devem lançar exceptions. Mas uso dentro do método lançamento de exception pra mapear os erros, no caso, lanço a exception e capturo no msm corpo de método ou capturo exception bubbling. 2. Método que tem retorno deve retorna Optional, a não ser que seja boolean. "Adeus nulllpointer😂" 3.Void podem lançar exception, pois não existe retorno pra validar a execução. 4. Method chaining podem lançar erros. Mas deve seguir o "porém" da regra 1. 5. Seguir o padrão do código existente, acho meio q tem prioridade
Tem outro q é um pouco difícil de seguir por causa dos frameworks. 6. Value Objects devem possuir "estado ausente" com Optional e Valor default. Meio q gosto de usar: Object.None() // retorna Optional.empty Object.withDefaults() Mas isso é outros 500😅
bom, ponto. Sempre usei o try-catch e throws. Concordo que isso pode não se encaixar como exception, já que é uma regra de negocio e algo esperado. Pra adicionar mais uma opção: já vi em alguns lugares o próprio objeto 'Oferta' ter um atributo 'erro' pra repassar o motivo.
Opa, esse do retorno carregar uma informação para controle de erro sinto que também é um puxadinho. Uma informação que não tem a ver com nada que está ali dentro :). Considero que coesão é uma das características mais importantes de ser mantida dentro do sistema.
Opa Alberto,
Excelente video, obrigado por compartilhar 👏🏻👏🏻
Tem algo (um argumento) que eh sempre confuso nessa discussão: "não use exception para controle de fluxo". Porém, isso está de certa forma incompleta. Na minha opinião, a mensagem correta do argumento deveria ser: "Use exceptions SOMENTE para controle de fluxos DE ERRO".
No seu video, vc fez correto, usou exception para controle de fluxo DE ERRO. Incorreto seria usar exceptions para outros tipos de fluxo de controle, como sucesso, repeat-loop, go-to etc.
Quanto ao debate de "use somente em casos excepcionais", essa eh outra discussao. 😬
No fim, tenho seu mesmo feeling: favoreça os contructos da linguagem em vez de tentar lutar com eles. Chuto que 99% dos casos será mais simples em diversos niveis do seu código (tente inibir o uso de exceptions com Spring Boot para você ver!).
Complementando...
Um bom design de código deixa claro para o client as boundaries para error handling, como a check-exception tenta fazer. O client (caller) está ciente das possibilidades de erro.
Porém em termos de experiencia eh MUITO ruim lidar com checked-exceptions (você comentou no seu video). Nao à toa a industria Java há 20 anos adotou UNchecked-exceptions como padrão, nao só no nivel da linguagem, mas tambem frameworks, libs, literatura etc.
Outro ponto, é que alguns autores recomendam usar UNchecked-exceptions para comunicação entre modulos de forma mais aberta e ampla, enquanto recomendam checked-exceptions apenas para comunicação em internal-modules. Voce até comentou no video algo do tipo também!
Sobre design de código...
Conseguimos melhorar o uso de UNchecked-exceptions declarando um `throws` no método e/ou na JavaDoc, mas sabemos que eh bem incomum (a industria não curte muito também hehe). Eh uma forma de compensar o que não temos nas checked-exceptions.
Ainda assim, tudo pode vir por agua abaixo se o dev(a) declara uma exception mais generica no `throws` ou esquece de adicionar todos os casos de erro no `throws` (novas exceptions). Isso mata o determinismo do modelo de erros pro client (caller).
Sei que vc discorda um pouco aqui. Mas nao ser explicito no `throws` não eh a pior a coisa do mundo em apps corporativas onde o dev tem acesso a todo o codigo fonte e muitas vezes ao autor do codigo. No mundo open-source a discussao eh outra, obviamente.
Enfim, lançar exceptions não eh algo ruim no mundo Java por motivos como:
1. eh proprio da linguagem;
2. a industria abraçou UNchecked-exceptions há quase 20 anos;
3. todo o ecossistema Java segue esse modelo de error handling;
4. as libs e frameworks que não seguem sao exceções a regra e geralmente mudam todo o modo de programar do dev(a);
5. lutar contra isso eh possivel, mas seria doloroso com libs populares como Spring, Hibernate e demais (novamente, tente inibir o uso de exceptions com Spring para ter uma ideia);
Aí você mitou, isso não é um comentário, é uma poesia! Valeu demais por somar tanto aqui com a gente.
Muito bom vídeo Alberto, obrigado por compartilhar.
Pensa num sistema grande, grande mesmo, eu trabalho em um hoje enorme, e temos devs de todos os níveis, júnior, pleno, senior, etc... nesse caso eu penso que usar o padrão da linguagem é melhor, pois não é necessário ficar explicando pra ninguém como tratar erros, usa o try/catch e pronto, todos já aprenderam assim.
Mas concordo que tem que tomar cuidado, nos code reviews precisamos ficar de olho se ninguém tá querendo controlar um fluxo com exception, mas sim tratar um erro de negócio (como o que você mostrou no vídeo).
Acho que concordo! Digo acho, pq realmente tem esses casos de exceptions como "go to". Mas foi aí que parei e pensei: A culpa não é da exception se ela é usada de maneira estranha hehe
Reflexão muito boa a respeito dos controles de fluxo, mas também em como refletir em padrões tido como "boas práticas" geram novas ideias e podem trazer melhorias para os nossos cenários específicos.
Concordo totalmente em utilizar as unchecked exceptions e anotando-as no método. Pode ser que alguns desenvolvedores não sigam a risca, mas aí entra no que foi comentado o problema ta em que tá usando e não na ferramenta. Além do mais se tivermos testes garantindo que tais exceções são tratadas, um dev desavisado vai ter que se esforçar muito mais para quebrar o padrão estabelecido no design do código, reduzindo tais cenários.
Valeu demais pelo comentário, Raphael!
Muito boa essa discussão. Vídeo excelente.
Na minha visão existem: Exceção de negócio e Exceção de sistema/código
Também é possível usar algum mecanismo de Validação como retorno.
Eu particularmente não gosto muito de Validação, geralmente dou preferência a Exceção de negócio.
Só parto para um framework de Validação quando se torna complexo com diversos retornos.
Exceção de negócio não é necessariamente algo totalmente inesperado, um fluxo apenas de erros, tem que ser um fluxo meio que excepcional, pois isso custa demais em tempo de processamento.
Opa, valeu demais pelo comentário. Tem esse lance do processamento mesmo. Ao mesmo tempo, tenho a impressão, aí falando apenas pelas minhas experiências, que para virar um problema precisa ser algo realmente tenso.
@@DevEficientenum sistema em que acabei criando algumas camadas, usei exceptions como controle de fluxo, a exception pulava diversas camadas e caia num tratamento global, a exception foi usada como um evento, realmente não vi muito problema no desempenho. O problema que notei foi que os outros desenvolvedores aparentemente estranhavam um pouco, estavam acostumados a entender a exception como erro de sistema exclusivamente.
Sempre trabalhei com checked exceptions em Java e vejo que a tipagem do Java ajuda demais a tratar os "erros previstos" cada vez que você usar o método que dispara as exceções checadas... Isso dá a oportunidade de quem usa o método, saber as possibilidades previstas.. Hoje trabalho muito com TypeScript, sinto muita falta das checked exceptions que não existem... Trabalho com golang também e tratamento de erros em Go é uma das partes que mais odeio... O padrão da linguagem é esse, mudar isso só se o benefício for muito maior do que a carga cognitiva de usar outro padrão...
Eu sou do time que gosta de checked exceptions tbm, os melhores projetos que trabalhei faziam bastante uso deles. Vejo bastante gente que não gosta, mas vejo tbm gente q sente falta qndo vai pra outros projetos heh
Legal demais o depoimento!
Muito legal!
Massa que curtiu!
Pra mim a regra é simples, se sua linguagem não suporta Pattern match exaustivo e robusto como Haskell e F# por exemplo não vale a pena usar Result.
Se até mesmo as "gambiarras" funcionais em C# e Java fornecidas pela linguagem podem tornar o código confuso algo feito pela comunidade (lib) tem muito mais chances.
Finalizando eu gostaria de falar que já tive esse mesmo dilema que tu apresenta no vídeo, a gente geralmente tenta encaixar coisas onde não dá, mas olhando o código a utilização do Result seria uma boa caso você tratasse ele como uma mônada para manter seu código puro, mas isso faria você ter que criar uma classe para identificar cada erro ou fazer uma factory para eles... além disso injetar funções anônimas torna o código menos legível e por isso fica mais confuso que o try catch e se você precisa saber qual o motivo do erro para tratar explicitamente no código, o jeito é ir para as Exceptions e tá tudo bem hshshsh
Hehehe, foi exatamente onde caí. Usei as exceptions para representar os erros e tratei os fluxos com funções... Aí parei e pensei: Poxa, era melhor usar exceptions.
legal, um ponto de vista diferente que complementou o meu entendimento que tinha do tratamento de erro do Clean Code, complementou o que eu ja pensava sobre também.
Massa demais que curtiu!
interessante
:)
Lembra das regras de complexidade que você indicava pontos de melhorias, quantidade de if, quantidade de linhas, chegou a fazer um template para sonarqube, meio que seria um lint centralizado das convenções, com um requisito para parar na pipe line de deploy, seria obrigatório os ajustes
Nunca fiz esse lint, queria tanto!
(Só assisti o video até os 9 minutos ainda)
Mas ai acho que o Java não mostrar que isso é um código morto vai mais do defeito da linguagem,
Se vc olha pra Rust por exemplo o retorno de erro é sempre um enum especifco, se vc parar de retornar aquele erro o typesystem vai te avisar que aquela branch não existe mais, o que casa ainda mais com o patten matching que possibilita vc tratar só algumas clausulas do erro.
Opa, é, a linguagem só apoia assim com checked exceptions :/
Sigo algumas regras:
1. Métodos que têm retorno não devem lançar exceptions. Mas uso dentro do método lançamento de exception pra mapear os erros, no caso, lanço a exception e capturo no msm corpo de método ou capturo exception bubbling.
2. Método que tem retorno deve retorna Optional, a não ser que seja boolean. "Adeus nulllpointer😂"
3.Void podem lançar exception, pois não existe retorno pra validar a execução.
4. Method chaining podem lançar erros. Mas deve seguir o "porém" da regra 1.
5. Seguir o padrão do código existente, acho meio q tem prioridade
Tem outro q é um pouco difícil de seguir por causa dos frameworks.
6. Value Objects devem possuir "estado ausente" com Optional e Valor default.
Meio q gosto de usar:
Object.None() // retorna Optional.empty
Object.withDefaults()
Mas isso é outros 500😅
Opa, acho que ter um guideline é massa demais! Apoio 1000%. Só fiquei em dúvida na segunda parte do 1. "Mas uso dentro do método..." Tem exemplo?
bom, ponto. Sempre usei o try-catch e throws. Concordo que isso pode não se encaixar como exception, já que é uma regra de negocio e algo esperado.
Pra adicionar mais uma opção: já vi em alguns lugares o próprio objeto 'Oferta' ter um atributo 'erro' pra repassar o motivo.
Opa, esse do retorno carregar uma informação para controle de erro sinto que também é um puxadinho. Uma informação que não tem a ver com nada que está ali dentro :). Considero que coesão é uma das características mais importantes de ser mantida dentro do sistema.