Fábio Akita abordou neste artigo um tópico sobre o qual sempre me interessei: educação. Um professor de universidade fez algumas perguntas com a intenção de entender como melhor investir na formação dos alunos.
A motivação, de acordo com o professor, é que muitos gerentes de TI reclamam que há um gap muito grande entre o que se ensina na academia e o que o mercado necessita…
Antes de entrar no mérito das perguntas do professor em si eu preferiria discutir o que eu considero o cerne da questão: qual o papel da universidade? Sem essa definição é simplesmente impossível orientar o professor ao caminho que ele deveria seguir para cumprir seus objetivos.
A motivação do professor, pelo que entendi do artigo, parece ser aproximar o aluno das necessidades do mercado. No entanto muitos professores de universidade com os quais eu conversei na época da graduação e mestrado parecem discordar desse objetivo. Para muitos professores o objetivo da universidade seria o de formar pensadores e não necessariamente de preparar o aluno para o mercado. Pelo menos não diretamente.
Há basicamente duas linhas de pensamento no que se refere ao papel da universidade. Há aqueles que acreditam que o profissional deveria sair da universidade pronto para atuar imediatamente no mercado e outros que acreditam que a universidade deveria ensinar o aluno a pescar, para resumir. Ou seja, o aluno teria uma base teórica que o permitiria a partir dali aprender quaisquer ferramentas técnicas que forem necessários para cumprir o objetivo de seu trabalho. Os que defendem essa linha frequentemente acreditam que se a universidade ensinar a usar as tecnologias de mercado atuais que estes alunos ficariam obsoletos daqui a X anos pois só conheceriam aquela tecnologia e não estariam preparados para se atualizarem. Esta também parece ser a opinião do Fábio Akita de acordo com o artigo.
Esse assunto é um tanto complexo e acho importante destacar tudo que está envolvido para que não fiquemos tentados a simplificar algo que não é simples. Vamos por partes.
Acho interessante iniciar a análise do tema do ponto de visto das motivações. É importante entendermos os objetivos de cada parte envolvida no processo de educação. Começarei pela parte que considero a mais simples e homogênea delas: o aluno.
A meu ver, a expectativa de 90% dos alunos pelo menos é a de, através do curso superior, preparar-se para o mercado com expectativa de receber melhores salários e condições de trabalho, que justifiquem o investimento de um tempo tão precioso. Ou seja, no máximo 10% dos alunos teria alguma intenção real em tornar-se um cientista investigativo, procurando inovar e entender conceitos complexos. A densa maioria quer um lugar no mercado, em empresas tradicionais que vendem produtos ou serviços e não em empresas voltadas para a pesquisa, o que não é apenas algo raro no mundo, mas especialmente no Brasil.
As outras partes do processo de educação não são nada homogêneas. Existem professores que defendem a aproximação da universidade com o mercado e outros que defendem que a universidade deve funcionar de modo totalmente independente do mercado atual. Da mesma forma está a distribuição de pensamentos entre as diversas pessoas envolvidas no processo, desde o governo, reitores, a população e os pais dos estudantes.
Para tornar a discussão ainda mais complicada há o agravante de que as universidades públicas e privadas funcionam normalmente de forma bem diferente devido à forma como elas são gerenciadas.
Nas universidades públicas os professores e funcionários receberão seus salários integralmente independentemente dos alunos serem aprovados ou não e por isso tem maior autonomia para decidir se o aluno está capacitado ou não para passar de ano, mas por outro lado podem até mesmo cometer abusos devido ao poder que lhes é concedido, além do corporativismo comum a esse setor, mas manterei o foco nas motivações neste artigo.
Por outro lado as universidades privadas visam maximizar o lucro em sua quase totalidade. Isso significa obter o maior número possível de pagantes usualmente, o que normalmente inibe as reprovações e por isso tentam todo tipo de artifício para fazer com que o aluno siga para o próximo ano, com avaliações extras, por exemplo, de modo similar ao que ocorre no ensino público fundamental, em que as estatísticas usadas para avaliação de indicadores são normalmente numéricas e é mais interessante mostrar que todos os alunos estão matriculados a prover uma educação de qualidade, que é difícil de ser medida. Objetivos diferentes mas resultados similares.
Algumas universidades no entanto conseguem ser rígidas e ao mesmo tempo lucrativas. Não sei de exemplos no Brasil, mas universidades como Harvard têm famas de serem difíceis e nem todos conseguem formar-se lá. Nesses casos o que ocorre é que a universidade por investir mais pesado na seleção dos que estão aptos têm maior credibilidade no mercado e o mercado estaria disposto a pagar mais por profissionais que estudaram nessas universidades. Por conta disso a universidade é capaz de cobrar mais caro de seus alunos.
No entanto não é simples chegar a esse ponto e muitas universidades lutam para sobreviverem e frequentemente acreditam que se tornassem o processo de avaliação mais rigoroso perderiam estudantes o suficiente para inviabilizar a instituição.
Normalmente as universidades privadas tendem a acreditar que a universidade deve formar profissionais para o mercado, pois é assim que eles justificam os preços de suas matrículas, através do retorno financeiro que seus alunos terão após o término do curso, em que estariam aptos a integrar o mercado de trabalho.
Esta forma de pensar conflita com a de muitos professores do setor público, que acreditam que as ferramentas atuais do mercado devem ser estudadas em paralelo de modo independente pelo aluno ou em cursos especializados após a conclusão da faculdade.
Essa é uma das áreas mais nebulosas pra mim. Acredito que a maioria dos políticos sequer reflita consideravelmente sobre a educação do país. Dos que tomam algum tempo para o assunto ou até mesmo dedicam-se a ele, as ideias são inúmeras.
Os recursos públicos são muito limitados e falta dinheiro para tudo no país. Assim sendo é preciso um estudo minuncioso sobre qual seria a melhor forma de investir o dinheiro arrecadado. No que se refere a educação frequentemente é necessário pensar em um foco. Ou você foca no ensino superior ou no ensino fundamental. No Brasil as regras são de que a esfera federal deveria preocupar-se com o ensino superior e tecnólogo enquanto os estados e municípios seriam responsáveis pelo ensino fundamental. Por outro lado, é possível encontrar artifícios para se subverter essa lógica. Por exemplo, o governo de FHC criou o Bolsa Escola, que era um programa social com recursos federais que não investia na educação de nível superior, mas na educação fundamental. A meu ver seria uma forma de driblar essa regra que impediria à esfera federal de investir no ensino fundamental.
Eu não sou especialista dessas regras e apenas reproduzo aquilo que ouço de terceiros mas o ponto é que sempre é possível usar de algum artifício para decidir onde colocar o foco da educação. No meu entendimento o ensino básico é o que mais carece de cuidado atualmente e deveria ser o foco da educação pública. O ensino superior é muito mais custoso e é capaz de atender apenas uma pequena parcela da sociedade. Dessa forma, a maioria é obrigada a pagar por um investimento do qual não irá usufruir. Para não politizar a discussão além da conta, prefiro apenas mostrar que dentro da esfera política também não está claro qual deve ser o papel da educação, e não apenas do nível superior mas como um todo.
De acordo com este artigo da Wikipedia, existe uma separação clara entre os dois tipos de curso superior em Portugal: o que eles chamam de ensino politécnico e ensino universitário. Eu não conheço a cultura portuguesa mas acredito que eles aceitem ambos os tipo de ensino de forma parecida, com salários associados de forma similar.
A cultura brasileira é a de considerar o ensino superior nas universidades como superior ao dos cursos tecnólogos pela grande maioria das empresas, que estariam mais dispostas a pagar mais aos profissionais formados em faculdades e valorizando menos os de nível técnico. Por esse motivo os alunos tendem a procurar mais o curso universitário com expectativa de melhores salários.
Essa é uma situação cultural infeliz porque muitas empresas realmente beneficiariam-se mais de tecnólogos para suprirem suas necessidades, que teoricamente estariam mais preparado para ingressar no mercado logo após a formação do que aqueles que se especializaram no processo científico em vez de nas ferramentas.
Voltando a discussão da educação para minha área de atuação profissional (minha área de formação é Engenharia Elétrica), e na condição de líder de um projeto grande há 3 anos que às vezes tem a oportunidade de entrevistar candidatos (formados ou não) além de ter tido contato em outras empresas com diferentes tipos de profissionais, eu procuro mostrar a minha percepção sobre esse mercado e o setor acadêmico.
Quando as pessoas consultam-se com um médico ou contratam um engenheiro normalmente assumem que o diploma é uma garantia da competência do profissional. Normalmente boa parte das profissões não é tão dinâmica quanto a de desenvolvedores de aplicações. As tecnologias nessa área mudam com uma frequência muito maior do que a da maioria das áreas em minha percepção. Por esse motivo, não é suficiente olhar que um profissional é formado na área para contratá-lo. Não é incomum ter contato com pessoas formadas na área e que têm dificuldades para concluir exercícios simples de programação usando as ferramentas solicitadas na descrição das vagas.
O candidato diz conhecer certa tecnologia mas ao ser colocado para fazer um exercício usando a tecnologia fica completamente perdido frequentemente. No fim, o processo seletivo acaba sempre criando sua própria avaliação pois a avaliação da universidade (pública ou privada) frequentemente não é suficiente. Nesse contexto eu questiono-me se de fato é interessante cobrar uma formação superior na área. E eu não sou o único. O número de profissionais sem formação atuando na área parece ser muito grande, assim como o número de profissionais da área proveniente de outras áreas de formação, como é o meu caso.
Além de ser possível trabalhar no mercado de programação de aplicações sem uma formação na área é também possível ter salários altos na área apenas demonstrando competência…
Nesse sentido é possível até questionar a viabilidade da maior parte dos alunos, que têm a motivação salarial e de carreira apenas, em fazer uma faculdade.
Afinal, com o sistema brasileiro de educação, uma pessoa só vai ter direito a alguma escolha (entre um curso técnico e uma universidade ou nenhuma especialização por exemplo) a partir da conclusão do segundo grau. Até lá não é facultado ao aluno escolher o que quer aprender. Quando o aluno termina o segundo grau frequentemente já atingiu a maioridade ou está para atingir.
E é meio que vergonhoso que a maioria das pessoas atualmente nessa condição não estejam trabalhando ainda, fazendo parte da cadeia produtiva. Em se tratando de classe média e alta é muito comum que muitos só venham a entrar para o mercado de trabalho depois dos 20 ou até mesmo 30 anos, sustentados por seus pais até então. Isso sugere que há algo muito errado no nosso sistema de educação a meu ver, por acreditar que a educação deveria preparar as pessoas para o mercado de trabalho e acredito que nós temos a capacidade de começar a integrar o mercado de trabalho já muito mais novos a partir dos 13 anos por exemplo e que é um erro levar tanto tempo antes de integrar o mercado de trabalho. Obviamente ninguém vai começar a atuar como contador ou médico aos 13 anos de idade, mas as pessoas podem começar a trabalhar com outras atividades que estejam a seu alcance. Na área de programação sequer há um limite para o que um profissional de 13 anos seja capaz de fazer.
Frequentemente as pessoas começam um estágio profissional em uma das áreas de formação de nível superior entre os 18 e 25 anos de idade, mas o mercado frequentemente considera os estagiários uma mão de obra barata dada a legislação vigente, o que não é muito interessante para os estagiários que recebem pouco e aprendem pouco.
Eu coloco-me à disposição para auxiliar o professor a reduzir o gap entre a universidade e as empresas, desde que fique claro qual é a expectativa do professor em relação à educação de nível superior. Eu não tenho como aconselhar a forma de lecionar e o tipo de conteúdo a ser explorado sem entender qual é o objetivo final do processo de educação esperado pelo professor. Uma vez que se esclareçam os objetivos eu tentarei colocar as mudanças que eu esperaria no sistema de educação para que os objetivos sejam cumpridos. Uma vez entendida a expectativa eu poderei responder às perguntas apontadas no artigo do Akita.
Atualização em 07/12/2012: a partir de hoje eu não sou mais cliente da GVT e nem pretendo voltar a ser. O atendimento piorou bastante desde que a GVT começou suas atividades em Vitória e eu vierei cliente deles. Hoje pela manhã eu precisei ir ao Procon para obter o extorno de uma cobrança indevida porque não consegui conversar com o setor de cobranças por telefone devido a esperas intermináveis sem que alguém atendesse à ligação. A qualidade técnica da GVT continua sendo melhor que a NET mas o atendimento da GVT está simplesmente insuportável! Segue o artigo original:
Qual a melhor provedora de internet? GVT ou NET?
Eu posso responder essa pergunta porque tenho as duas.
Eu trabalho em casa para um cliente nos EUA, através da minha empresa em Porto Alegre, morando em Vitória-ES.
É fundamental para meu trabalho que eu esteja conectado na internet via Skype e Gtalk durante meu expediente. Por isso, acabei contratando a NET, além da GVT que eu já tinha, para evitar ficar sem internet quando alguma delas sair do ar.
Isso ocorre com ambas, na mesma frequência, cerca de uma vez por mês ou um pouco menos. Mas pode durar o dia inteiro, como já vi acontecer com a NET. Na GVT, normalmente dura um expediente (uma manhã ou uma tarde).
GVT! Não tenho do que reclamar.
Minha velocidade contratada com ambas é de 10Mbps. Eu sempre costumo baixar a 1MB/s com a GVT ao atualizar meu Linux, correspondendo à velocidade contratada. No mesmo horário, e baixando dos mesmos servidores, a NET chegava a baixar a 100KB/s e até mesmo com cerca 30KB/s com certa frequência.
Eu contrato o serviço de chamadas por telefone através do Gmail. Com ele, eu posso pagar US$0,01 por minuto para qualquer telefone nos EUA, US$0,03 para qualquer fixo no Brasil (para algumas cidades é um pouco menos) e US$0,15 para qualquer móvel no país. O único problema é: não funciona com a NET.
Quando liguei para avisar a NET desse problema com o serviço de chamadas do Gmail, uma atendente disse que alguém da área técnica entraria em contato comigo dentro de no máximo meia hora. Estou aguardando até hoje. Caso alguém tenha curiosidade, o número do protocolo da conversa é 508110131300141.
Enfim, se vocês não podem se dar ao luxo de ter duas provedoras de internet (eu reconheço que sou minoria), aconselho que fiquem com a GVT.
Boa escolha!
Gerenciar uma equipe de desenvolvedores de sistema não é uma tarefa simples. Há muitas variáveis envolvidas e a ciência está longe de ser considerada exata. Eu nunca trabalhei como gerente de projetos de software nem tenho intenção de fazê-lo, no entanto, trabalho há muito tempo como desenvolvedor tanto autonomamente quanto como parte de uma equipe gerenciada. E sei com clareza o que não funciona e por quê na área de gerenciamento de projetos de software.
Nota do autor: leia esse artigo quando estiver com tempo, pois o assunto é complexo e não sei abordá-lo de modo mais resumido que já está.
Gostaria eu de ter a fórmula mágica para orientar os gerentes a desempenharem sua função de modo perfeito. Infelizmente eles terão de encontrar seu próprio caminho, mas eu posso ajudar em um tópico no qual tenho experiência. Eu sei como funciona a cabeça de um desenvolvedor e como procurar obter melhor produtividade de uma equipe de desenvolvedores.
Tradicionalmente, a disciplina de gerenciamento de projetos vem sendo tratada como uma tarefa quase exata com uma série de procedimentos claros de como gerenciar tempos e recursos (desenvolvedores), inspirada em modelos de organização industrial desenvolvidos por engenheiros na época da revolução industrial, com foco na automação de tarefas. Tais conceitos eram empregados em otimizações de processos industriais.
Essas técnicas ajudaram a melhorar a eficiência de recursos e aumentar a produção em indústrias automobilísticas, fábricas de manufatura e outros empreendimentos. Como diz meu professor, engenheiro e amigo Hansjörg Andreas Schneebeli, que me orientou no mestrado em Automação no curso de Engenharia Elétrica: “Para quem tem martelo na mão tudo é prego”.
Quando os sistemas de informação surgiram, não havia cursos específicos voltados para a Computação. Os profissionais desta área possuíam formação de Engenharia na maioria. E com isso, é natural que tenham tentado empregar técnicas de Engenharia para resolver problemas relacionados à área de desenvolvimento de sistemas, não apenas nas técnicas de codificação dos sistemas, mas também nas técnicas de gerenciamento de projetos.
Este equívoco não é privilégio da área de Computação mas a maioria dos gerentes de projetos nos diversos setores emprega esse modelo tradicional como se eficaz fosse para a resolução de problemas que exigem não só conhecimentos técnicos específicos da área, mas conhecimentos de Ciências Humanas com igual proporção.
O Movimento Ágil percebeu há uma década que o modelo tradicional não tinha como ser aplicado com sucesso na área de projetos de sistemas informatizados. Deste movimento surgiram recomendações como Scrum, Lean e XP. Não comentarei sobre essas técnicas pois a literatura é farta com bons artigos e livros disponíveis para os interessados.
Em vez disso, como percebo que a metodologia tradicional de gerenciamento de projetos ainda predomina, com uso de ferramentas como MS Project e outras similares, tanto para ambientes Desktop e web, eu preferi focar nos motivos pelos quais esta metodologia não funciona para desenvolvimento de sistemas de acordo com observações pessoais que venho fazendo ao longo dos anos enquanto desenvolvedor.
Programas como o MS Project baseiam-se principalmente na ferramenta excelente que é o Diagrama de Gantt. Por melhor que o martelo e o alicate sejam boas ferramentas, eles não ferramentas adequadas para se apertar um parafuso. Infelizmente as pessoas não percebem com a mesma clareza que o mesmo ocorre com Diagramas de Gantt e desenvolvimento de sistemas. É preciso entender o problema de desenvolvimento de sistemas para perceber que ele difere significativamente dos processos de produção nos quais Diagramas de Gantt têm especial utilidade.
Resumidamente, o diagrama de Gantt busca reduzir o tempo de entrega de um projeto identificando as interdependências entre tarefas e os recursos necessários para que cada tarefa seja concluída. Com a estimativa de tempo necessário para conclusão de cada tarefa e identificação dos recursos necessários para cada tarefa é possível estimar o prazo final para conclusão do projeto inteiro, bem como identificar gargalos e acompanhar o andamento do projeto.
Para os investidores financeiros, essa é uma ferramenta ideal pois eles podem compreendê-la e estabelecer uma linguagem comum com os gerentes de projetos e utilizá-la para calcular o custo de um projeto e analisar sua viabilidade técnica e financeira, além de acompanharem o andamento do projeto.
Portanto, pela descrição acima, é natural pensar que tal ferramenta caia como uma luva para gerenciar projetos de software, considerando os desenvolvedores como recursos para conclusão das tarefas de desenvolvimento, reparo ou evolução de um software. Além disso, seria possível identificar quantos desenvolvedores são necessários para concluir um projeto em determinado prazo ou estimar qual o prazo de entrega de um projeto baseado nos recursos existentes.
Essa ferramenta não funciona quando aplicada a desenvolvimento de sistemas. A prova disso é o comentário típico que já ouvi de diversas pessoas em diversas empresas por diversas vezes: “A gente faz a estimativa e passa o dobro do prazo para o cliente”.
Utilizar uma “gordurinha” de 100% é o mesmo que confessar que a estimativa não funciona. Até porque mesmo com a “gordurinha” o projeto costuma levar o dobro do tempo passado com a gordura — quando é concluído — e raramente funciona conforme previsto inicialmente.
O problema com essa abordagem é que os pressupostos são falsos. Para que os diagramas de Gantt sejam úteis, é necessário que pelo menos os seguintes pressupostos sejam atendidos:
Outros erros de entendimento comuns ao tentar aplicar tais diagramas:
Além disso, em ambientes de desenvolvimento de sistemas, frequentemente existem outros fatores:
Na RubyConf Brasil 2010, eu presenciei na “desconferência” um discurso de Diego Carrion, que comentou sobre diversas situações que foram observadas em sua empresa. Ele iniciou a fala dizendo que o objetivo de sua empresa era “ganar dinero” com seu sotaque argentino. Apesar dele ser naturalmente engraçado tanto pelo sotaque como por sua pessoa, ele comentou com humor sobre vários assuntos sérios e o humor vinha justamente do fato que o que ele estava dizendo era realmente verdade. Foi quando ele soltou uma frase mais ou menos assim, que eu não esqueci mais:
Nós tentamos estimar prazos de entrega utilizando diversas técnicas, incluindo aquelas aconselhadas por metodologias ágeis como Scrum e XP. Todas falhavam feio. Após muitas tentativas, conseguimos finalmente obter uma taxa maior de acerto nas estimativas de tempo para cada tarefa: compramos um par de dados.
Eu achei fantástico porque realmente em muitos casos, principalmente no início de um projeto, estimar tempo de conclusão de tarefas é como jogar dados. E este é um dos motivos que as iterações em projetos que se orientam pelo Scrum ou XP não costumam ultrapassar 2 meses, sendo recomendado o prazo de 15 a 30 dias entre cada entrega (sprint).
O grande problema da estimativa de software é que ele não é entendido por pessoas de outras áreas. Muitas vezes não é entendido até mesmo por profissionais do ramo. As empresas estão acostumados a comprar produtos, onde existe um valor, um prazo de entrega e uma especificação clara. Toda sua ferramenta de análise financeira e organizacional baseia-se nisso.
Elas ainda não estão acostumadas a comprar serviço. Alguns tipos de serviço são bem entendidos e se encaixam nas ferramentas com as quais essas empresas já estão habituadas, como faxina, buffet, locações, etc. No entanto, trabalhos de arte sempre sofreram com problemas de estimativa, valor e escopo. Você pode desenhar um quadro, estipular um preço e transformá-lo em um produto e procurar por um cliente interessado.
Mas existem certos trabalhos, como em Marketing, onde alguém lhe pedirá para fazer um panfleto com uma determinada ideia e você precisará especificar valor e prazo para entrega. Nesses casos, o profissional não sabe previamente como será o panfleto nem se ele estará de acordo com a expectativa do cliente. Se o cliente recusar seu trabalho, ele precisará adaptá-lo e aí fica a questão de como computar essas alterações na estimativa.
Um problema semelhante ocorre com desenvolvimento de software. São tantas as semelhanças entre os dois tipos de trabalho que muitos defendem (incluindo eu mesmo) que desenvolver sistemas é muito mais um trabalho de arte do que uma ciência exata. Na verdade, está muito longe de ser uma ciência exata. Trabalhos de arte também envolvem ciência nas várias técnicas que precisam ser estudadas para serem bem empregadas, assim como existem várias técnicas e estudos na área de desenvolvimento de sistemas.
Quando se constrói um edifício ou uma cadeira, normalmente vários outros similares foram construídos. De acordo com a média de tempo das construções já realizadas é possível estimar quanto tempo será necessário para uma nova solicitação. O mesmo não costuma acontecer com softwares. Até porque problemas bem entendidos costumam levar a construções de bibliotecas para serem reutilizadas em sistemas futuros. Ou seja, praticamente todo desenvolvimento é novidade e por isso não é possível estimá-lo com base em projetos anteriores.
Se alguém solicitar uma aplicação que realize as 4 operações básicas (criar, alterar, excluir e visualizar), alguém poderia arguir que é perfeitamente válido dar uma estimativa para essa tarefa uma vez que todo desenvolvedor já fez uma aplicação assim várias vezes. De fato é tão comum que a maioria dos frameworks permite criar um esqueleto de um sistema desse tipo completo com um único comando, instantaneamente. Até mesmo validações usuais são simples de serem acrescentadas a tais sistemas. No entanto, eu nunca vi uma aplicação real na qual um sistema gerado dessa forma atendesse completamente às expectativas do cliente.
Diversas propostas surgiram para tentar estimar o tamanho de um software e com isso adequar-se às necessidades das empresas em obter um valor fixo e um prazo fixo para um escopo fixado. Foram criadas técnicas como contagem de pontos de função e contagem de casos de uso. Quando trabalhei na Fundação de Apoio à Ciência e Tecnologia do Espírito Santo (FAPES), fui solicitado para especificar o sistema NOSSABOLSA. Feita a especificação técnica do sistema, a lei do estado exige um parecer do PRODEST, o qual solicitou que fosse incluído no edital a manutenção do sistema. A SEGER então orientou que se utilizassem pontos de função para todo edital que envolvesse manutenção de sistemas.
Eu nunca havia ouvido falar sobre pontos de função até então e precisei pesquisar sobre o assunto. A ideia é que o sistema permite pontuar um projeto de acordo com suas funcionalidades e complexidade, avaliadas de modo independente de tecnologia e quantidade de pessoas envolvidas. É a ferramenta ideal para um gestor de governo poder inclusive comparar diferentes tecnologias (aquela capaz de concluir o mesmo número de pontos de função no menor tempo).
Interessantemente, em todo material que pesquisei, uma regra básica é que pontos de função não podem ser utilizados para manutenção de sistema relacionadas a correção de bugs ou pequenos ajustes. Na prática, é impossível mensurar um sistema excluindo-se fatores fundamentais como tecnologia, equipe técnica, formas de interação entre o cliente e empresa contratada, entre diversos outros fatores.
Eu poderia comentar sobre os vários motivos pelos quais pontos de função, casos de uso ou qualquer outra métrica desse tipo não funciona, mas eu perderia o foco do assunto principal deste artigo e essa discussão precisaria de muito espaço aqui. Apenas acreditem que é impossível estimar alguma entrega para mais de 3 meses (a partir de um mês já é muito difícil).
A ideia deste artigo é focar nos principais erros cometidos por gerentes de projetos que observei na prática.
“Testes automatizados são interessantes, mas não temos tempo para escrevê-los agora. Vamos deixá-los para depois.”
“Eu reconheço que a solução ideal é tornar esse campo configurável mas isto exigirá uma hora extra. Por enquanto altere o valor direto no código e quando houver tempo nós o tornamos configurável.”
“Eu entendo que a linguagem/framework/tecnologia utilizada no momento não é a ideal para o projeto mas não teremos como refatorar o sistema agora. Quando houver tempo nós o faremos.”
“Realmente, o código está desorganizado mas mantenha-o assim por enquanto até termos um tempo para organizá-lo.”
Todo desenvolvedor de uma equipe já deve ter ouvido frases similares a esta diversas vezes em seu tempo de profissão. O que eu nunca ouvi foi: “Ok, agora que estamos com mais disponibilidade de tempo vamos quitar nossas dívidas técnicas”.
O problema com essas “gambiarras”, às quais alguns desenvolvedores referem-se como “recursos técnicos” e que na realidade são dívidas técnicas, é que elas engessam cada vez mais e com maior velocidade a evolução de um sistema. E elas não são sustentáveis. Você não pode ignorá-las por muito tempo ou seu sistema ficará rapidamente estagnado e repleto de bugs.
É muito importante para um gerente de projeto entender isso, confiar mais nos desenvolvedores e dar-lhes tempo para quitar suas dívidas técnicas. Gerente e desenvolvedores são parceiros e precisam ajudar-se mutuamente. É um erro partir do princípio que os desenvolvedores só olham o lado deles e que não entendem a importância de cumprir prazos. Acontece que muitos deles já tem experiência suficiente para saber quando adiar a dívida técnica é a pior opção e que os prazos serão cada vez menos cumpridos enquanto essas dívidas não forem quitadas.
É importante que os gerentes confiem em seus desenvolvedores e não os pressione a aumentar ainda mais suas dívidas técnicas. Discuta como está a situação atual do projeto, as expectativas do cliente e definam o que é ou não possível entregar e qual será a estratégia de desenvolvimento. Em caso de se decidir por aumentar a dívida técnica, deve-se também estipular o momento em que tais dívidas serão quitadas e quanto tempo será necessário para quitá-las.
Se um desenvolvedor não se preocupa com os prazos de um projeto ou não quer trabalhar, não há alternativa senão dispensá-lo. É um erro acreditar que eles produzirão mais se forem pressionados. Quem não quer trabalhar sempre achará um jeito de não fazê-lo. Ao perceber essa atitude em um desenvolvedor, o gerente deve conversar seriamente com ele, ameaçando-o dispensá-lo se ele não mudar sua atitude e realmente fazê-lo se ele insistir na atitude. Pressioná-lo simplesmente não resolverá o problema e só criará mais problemas.
Outro erro frequente é acreditar que desenvolvedores são equivalentes e que são escaláveis. É comum encontrar hierarquias como Desenvolvedor Júnior, Desenvolvedor Pleno, etc, mas a forma de se avaliar esses níveis não funciona normalmente. Você pode classificar Portinari, Pablo Picasso e Michelangelo como pintores plenos. Ainda assim eles terão estilos completamente diferentes tanto no modo de trabalhar como no resultado gerado em si. Você pode resolver alocar os 3 pintores para uma única grande obra na tentativa de concluí-la mais rapidamente e com qualidade. No entanto essa lógica pode ou não funcionar dependendo das habilidades desses pintores de trabalharem em equipe e da compatibilidade do trabalho deles.
Da mesma forma, na arte de desenvolvimento de sistemas, existem diferentes pessoas com diferentes habilidades, tanto técnicas quanto sociais. Encontrar uma boa equipe que trabalhe bem junta é uma dádiva e quando isso acontece, deve-se tentar preservá-la. Basta uma pessoa para estragar uma equipe bem sucedida. O gerente deve estar atento a essas situações para corrigi-las assim que forem percebidas.
É importante entender que uma equipe de desenvolvimentos lida com indivíduos, e como o o nome já diz, eles devem ser tratados de modo individual. É um erro acreditar que no momento em que for necessário acelerar um projeto bastará incluir mais 1 desenvolvedor pleno e 2 juniores para que o projeto seja entregue 2 meses antes do que seria com a equipe atual. É necessário tempo até que um desenvolvedor recém-integrado a um projeto comece a apresentar resultados, por melhor que seja. A existência de testes automatizados pode reduzir significativamente esse tempo de adaptação, mas ele ainda existirá.
Interrupções são o pior inimigo da produtividade. Existem diversos estudos e artigos sobre esse assunto e não os repetirei aqui:
1- Multitasking Gets You There Later 2- Need an Answer to Context Switching? Get Disturbed 3- Human Task Switches Considered Harmful 4- The way I work? Interruption is the enemy of productivity 5- A diary study of task switching and interruptions 6- Project Management and Task Switching
Existem certamente diversos outros artigos similares e a ideia aqui é mostrar como o assunto é tão importante que merecem artigos e estudos completos sobre o assunto. O segundo artigo ainda sugere que seja alocado um membro da equipe apenas para ajudar os demais, sendo elegido o “perturbado”, do qual não se esperará qualquer trabalho sério de desenvolvimento.
Por isso é importante que a equipe de desenvolvimento seja interrompida o mínimo possível. Desenvolvedores como eu perceberam o quanto um programa leitor de notícias ou um cliente de e-mail aberto em background acabam com a produtividade toda vez que exibem uma notificação de novo artigo ou e-mail. Se for realmente importante interrompê-lo no meio de uma sessão de análise ou codificação, procure utilizar um programa de mensageria instantânea, dando preferência ao uso de servidores usados apenas na intranet de sua empresa. Na empresa em que trabalho, utilizamos o Openfire que pode ser instalado em menos de 10 minutos, além do tempo da criação das contas de usuário.
Em último caso (de vida ou morte) acione o desenvolvedor por telefone ou pessoalmente. Procure estabelecer um horário do dia com a equipe para reportação de status do projeto. Estas micro-reuniões não deveriam passar de 15 minutos. Se algum tópico precisar ser melhor discutido, termine a reunião com a equipe e inicie a discussão apenas com os desenvolvedores diretamente envolvidos na resolução do problema, em um ambiente isolado do resto da equipe.
Muitos gerentes de projeto não confiam em seus desenvolvedores. Eles acreditam que o único meio de exigir trabalho deles é através da vigilância. Eles irão frequentemente verificar o que um desenvolvedor está fazendo e se ele está trabalhando em um projeto. Em algumas empresas ou órgãos governamentais, existirão ainda regras restritivas de acesso à Internet, ou registro do conteúdo acessado através de um proxy.
Esse é justamente o pior ambiente criado na tentativa de maximizar a produção dos desenvolvedores. Bons desenvolvedores não se sentirão motivados em um ambiente de trabalho com esse tipo de controle e irão certamente procurar outros ambientes para trabalhar. Essa é justamente a receita que fará com que sua equipe seja formada pelos mais medíocres desenvolvedores. E o resultado que se pode esperar vindo de desenvolvedores medíocres é obviamente medíocre.
Tampouco esse controle será eficiente em melhorar o desempenho de sua equipe de desenvolvedores. No entanto, eu reconheço a importância em se acompanhar de perto a evolução de um projeto. Para isso existem excelentes ferramentas gratuitas e comerciais como o Chiliproject / Redmine + ScrumPM plugin, Pivotal Tracker, ScrumDo entre várias outras.
Independentemente da abordagem escolhida para gerenciamento de projetos, seja ela XP, Scrum, Kanban, Lean ou qualquer outra similar, existem excelentes ferramentas disponíveis tanto gratuitamente quanto comercialmente. Essas ferramentas são interessantes pois ajudam os desenvolvedores e permitem que os gerentes acompanhem o andamento sem precisarem interromper a equipe frequentemente.
TODO: Falar sobre como é importante para um desenvolvedor estar atualizado e como esta hora deve ser computada.
TODO: Falar sobre as atribuições do gerente nesse aspecto.
TODO: Falar sobre quem deveria fazer a seleção do candidato, da importância das contratações e da relação entre valores de salários vs benefício sobre diferentes níveis de profissionais.
TODO: Falar sobre generalistas vs especialistas e times grandes vs pequenos
TODO: Comentar sobre Pair-Programming e seus benefícios e de como deveria ser o ambiente de trabalho, bem como horas extras e intervalos.
TODO: Explicar sobre esse assunto
TODO: Falar sobre report status / daily meetings.
TODO: Falar sobre promoção dos testes automatizados e importância de código bem escrito.
TODO: Falar sobre capacitação constante e da necessidade de reciclagem dos desenvolvedores e aprendizado contínuo. Incentivar compra de livros, materiais didáticos e investimentos em cursos.
TODO: Promover uma tarde para que membros de uma equipe possam apresentar uma tecnologia ou ferramenta aos demais, como ferramenta de nivelar conhecimentos na equipe e promover discussões.
TODO: Frear o setor comercial na tentação de pegar mais atividades do que a equipe é capaz de lidar de modo sustentável.
TODO: Falar sobre fixar um tempo para leitura de e-mails e agregadores de notícias.
TODO: Falar sobre comportamentos a serem evitados como comparações/reclamações de salários ou críticas a outros setores.
Existem alguns sites brasileiros que utilizam certificados assinados apenas pela Autoridade Certificadora Raiz Brasileira, a qual não é incluída no Google Chrome automaticamente. Com isso, sites como o Internet Banking da Caixa Econômica Federal, por exemplo, ficam com um cadeado com um X e informam que o certificado não é seguro. Para resolver esse problema, basta importar o certificado da AC Raiz para o Google Chrome.
Salve todos os certificados da AC Raiz da ICP-Brasil do repositório oficial do governo. Só é necessário baixar aqueles cuja descrição do link começa com “Certificado da AC Raiz da ICP-Brasil…”. Na data de escrita deste artigo, são 4 links no total.
No menu Configurações (acesse o endereço chrome://chrome/settings se não encontrar), pesquise por “certificados” e clique no botão “Gerenciar certificados…”.
Clique na aba “Autoridades” e em seguida no botão “Importar…”. Escolha um dos certificados gravados no disco. Você deverá então marcar pelo menos a primeira opção: “Confiar neste certificado para a identificação de websites.”. Eu preferi marcar todas as opções, mas as outras são opcionais para a verificação de sites seguros.
Repita o procedimento para cada um dos outros certificados. No caso da Caixa Econômica, seus certificados estão na cadeia v1. Nesse caso, apenas este certificado já basta para que o cadeado fique verde.
Por muito tempo insisti em tentar utilizar IDEs como Netbeans, RubyMine, Eclipse e IntelliJ IDEA. Todas têm o mesmo problema: consumem muito recurso, são lentas e implementadas em Java (leia-se: você nunca sabe quando ocorrerá a próxima coleta de lixo e normalmente isto acontecerá no momento de maior inspiração)…
No último sábado minha irritação foi tanta que resolvi investir praticamente 3 dias completos do meu feriado estudando vários plugins para o Vim na tentativa de torná-lo um ambiente produtivo para desenvolvimento. O resultado foi satisfatório e resolvi escrever sobre como obter produtividade com Vim, resumindo os recursos interessantes que aprendi nesses 3 dias.
Caso você já seja usuário do Vim, recomendo que faça um backup dos seus arquivos de configuração caso deseje testar a configuração que descrevo a seguir.
Os recursos que utilizava nos IDEs e que consegui utilizar no Vim foram:
Além desses recurso, conheci outros recursos que nunca cheguei a utilizar nos outros IDEs que utilizei. Seguem os detalhes.
As instruções que descrevo foram as que utilizei com a distribuição do Linux que uso (Debian unstable) mas também deve funcionar com o Ubuntu e a maioria das distribuições Linux. No Windows, aparentemente a mudança é que o diretório de configuração do Vim chama-se “vimfiles” em vez de “.vim”. Caso tenha alguma dúvida sobre o processo de instalação, é só postar um comentário.
Instale os pacotes necessários (como root, ou use “sudo” no Ubuntu):
1 | apt-get install exuberant-ctags vim-gtk git |
2 | cd |
3 | git clone --recursive git://github.com/rosenfeld/vimfiles.git .vim |
4 | ln -s .vim/vimrc .vimrc |
5 | mkdir .vim/spell |
6 | wget -O .vim/spell/pt.utf-8.spl http://github.com/rosenfeld/git-spell-pt-br/raw/master/pt.utf-8.spl |
Algumas notas (para os curiosos ou quem tiver problema em executar os passos acima):
Após esses procedimentos, basta executar o Vim (sugiro utilizar o comando ‘gvim’ para evitar problemas com atalhos).
O Vim tem muito mais recurso do que o que apresentarei. Para os que tem tempo disponível, recomendo a leitura de outros materiais a respeito.
Diferentemente de outros editores, o vim possui diferentes modos. Ele é iniciado no modo normal, aguardando uma sequência de caracteres que representem uma ação. Pressionando ‘i’ ou ‘Insert’, o vim entra no modo de inserção, a partir do qual é possível digitar qualquer coisa. Para sair do modo de inserção, basta pressionar ‘Escape’.
Boa parte dos comandos são executados a partir de uma linha de comando que surge ao pressionar a tecla ‘:’. Alguns comandos corriqueiros são:
No modo de edição, é possível executar um comando digitando Ctrl+O e o atalho do comando no modo normal. Há várias formas de entrar no modo de edição, quando se está no modo normal:
Para excluir linhas, palavras e blocos, gerenciar cercas (surrounds) e inserir comentários:
No modo normal (não utilizar o ‘:’):
No modo visual, para copiar basta pressionar ‘y’ após selecionar o trecho, ou ‘“+y’ / ‘”*y’ para copiar para os registradores equivalentes às área de transferências comum e do mouse respectivamente.
Já comentei sobre alguns comandos básicos relacionados a abas. Seguem outros mais avançados:
O atalho ‘<c-x><c-f>’ (Ctrl+X Ctrl+F) ativa a busca rápida de arquivos para edição.
Dentro de um diretório do projeto em que você está trabalhando, o vim exibirá uma lista de arquivos que vai sendo filtrada na medida em que se digita partes do arquivo. Por exemplo, ao digitar ‘a/c/uc’, ‘app/controllers/usuario_controller.rb’ apareceria como uma possibilidade.
Selecionando com Enter, o arquivo será aberto na área atual. Pressionando Ctrl+t, o arquivo será aberto em uma nova aba.
Snippets são trechos de códigos expandidos ao se pressionar TAB. Por exemplo, div<TAB> está configurado para expandir para <div id=“?”>?</div>.
Para conhecer os snippets ou criar novos, verifique os diretórios snippets em ~/.vim/bundle/snipmate/snippets e ~/.vim/bundle/rosenfeld/snippets.
Os snippets são lidos automaticamente de bundle/*/snippets e ~/.vim/snippets.
Atalhos para se trabalhar com HTML/XML também se aplicam a arquivos PHP, ASP, ERB, JSP, etc, desde que o tipo de arquivo seja algo do tipo “html.erb”. Isto pode ser obtido com o comando :set ft=html.erb, para arquivos ERB. Para configurar estes tipos automaticamente de acordo com a extensão do arquivo, veja alguns exemplos em ~/.vim/filetype.vim.
Alguns atalhos para se trabalhar com HTML já foram citados, seguem alguns outros, para o modo de inserção:
Para arquivos de template, como ERB, JSP, PHP, etc:
Para ERB (Ruby), criei os seguintes snippets equivalentes:
Para escolher uma cor ao editar um arquivo CSS, com o kcolorchooser instalado (ver ~/.vim/initializers/kcolorchooser-mapping.vim para alterar programa de escolha de cor) pressione F12.
Comandos:
Conheço duas formas de se trabalhar com tags no vim:
Comandos:
Deve-se criar um arquivo tags no diretório corrente (digite ‘ctags –list-languages’ para ver as linguagens suportadas):
1 | ctags -R --languages=Ruby,Javascript |
Groovy não é suportado por padrão pelo exuberant-ctags, mas adicionar este conteúdo a ~/.ctags parece funcionar. Você pode fazê-lo com este comando (no Linux ou Mac):
1 | curl https://raw.github.com/gist/2142910/ctags >> ~/.ctags |
A partir daí, para pular para a definição de uma tag onde o cursor se encontra: - Ctrl+] ou Ctrl+<LeftMouse> ou g<LeftMouse>: pula para a definição na janela corrente - Ctrl+T ou Ctrl+<RightMouse> ou g<RightMouse>: Volta para a posição anterior ao pulo - Ctrl+w, ]: divide a janela horizontalmente e pula para a definição - g, Ctrl+] e Ctrl+w, g, ]: Apresentam uma lista de definições antes de pular se houverem múltiplas definições - ‘:tag NomeTag’: Vai para a definição de ‘NomeTag’ - ‘:ts NomeTag’: Abre uma lista de definições encontradas e lhe dá a opção de escolher uma - Ctrl+: vai para a definição em uma nova aba
Comandos:
Comandos:
Comandos:
Comandos:
Comandos:
No navegador:
Para executar um comando externo:
Veja $VIMHOME/bundle/vcscommand/doc/vcscommand.txt para outros comandos. Por exemplo: - \cd: exibe as diferenças do arquivo corrente em uma nova divisão horizontal - \cr: exibe a última revisão do arquivo em uma nova divisão horizontal
Suponha que você queira saber quais são as diferenças entre suas mudanças não gravadas e o arquivo original: - \cr: exibe o arquivo original em uma nova divisão horizontal. Se você quiser que a divisão seja vertical, mova a janela para a esquerda (Ctrl+w, H) ou direita (Ctrl+w, L). H e L devem ser maiúsculas. - execute ‘:diffthis’ em ambas as janelas: veja próximo tópico sobre as diferenças.
Você também pode dar uma olhada em $VIMHOME/bundle/fugitive/doc/fugitive.txt para mais atalhos para trabalhar com Git,como: - ‘:Gstatus’: exibe a saída de ‘git status’ e permite-lhe adicionar ou remover o arquivo sob o cursor do próximo commit pressionando ‘-’, ou ver as diferenças em uma janela vertical (pressionando ’D') ou em uma janela horizontal (pressionando ‘dh’). - ‘:Gcommit’, ‘:Gblame’ and ‘:Gmove’ são outros exemplos auto-explicativos. Dê uma olhada na documentação do fugitive para mais detalhes.
Abra ao menos duas janelas com os textos que se quer comparar e digite ‘:diffthis’ em ambas. Para desabilitar o recurso use o comando ‘:diffoff’. Utilize ‘dp’ em uma diferença destacada para por a versão corrente da diferença na outra janela ou ‘do’ para obter a diferença da outra janela. Use ‘[c’ e ‘]c’ para ir para a mudança anterior e próxima respectivamente. Veja ‘:h diff’ para mais detalhes.
Comandos:
Em projetos Rails (use tab para auto-completar a maioria dos comandos):
Depuração:
Você precisa instalar a gem ‘ruby-debug-ide19’ or ‘ruby-debug-ide’ para isto funcionar:
Embora Vim não lhe permita refatorar uma variável diretamente (ao menos, eu não sei como fazê-lo no Vim), ele pode ajudá-lo a refatorar seu código de diversos modos, usando comandos de substituição até extração de variáveis como no exemplo abaixo:
Suponha que você queira refatorar o código abaixo como segue:
1 | if User.find(params[:id]) and current_user.admin? |
2 | # ... |
3 | end |
para:
1 | @user = User.find(params[:id]) |
2 | raise NotFoundException unless @user and current_user.admin? |
3 | # ... |
Para extrair a parte “User.find(params[:id])” para a variável “@user”, você pode posicionar o cursor sobre o “U” e executar os comandos “c% @user” (alterar o conteúdo “User.find(params[:id])” com “@user”), “Ctrl+o, O” (executar o comando ‘O’ no modo de inserção [Ctrl+o] - criando uma linha acima da atual), digitando “@user = ” e ‘Ctrl+R “’ (colando o conteúdo recortado anteriormente).
Com todas essas explicações, pode ter parecido complicado, mas veja como podemos alcançar o objetivo com apenas algumas combinações de teclas:
c% @user
Aprender a utilizar o Vim em sua expressão máxima permitir-lhe-á realizar várias tarefas mais rapidamente que qualquer outro editor ou IDE de um modo geral. Por exemplo, RubyMine permitir-lhe-á fazer o mesmo com provavelmente menos combinações para este caso específico, mas para casos especiais o Vim ainda lhe será bastante útil e não muito menos produtivo que o RubyMine para o caso comum. Na verdade, recortar “User.find(params[:id])” é muito mais rápido no Vim (“c%”) do que selecionar o texto inteiro no RubyMine ou qualquer outra IDE. O mesmo se aplica para se alterar o conteúdo dentro de aspas, parênteses, tags XML, etc entre outros recursos.
Infelizmente, nem tudo são flores. Alguns recursos interessantes dos IDEs tradicionais ainda não consegui colocar para funcionar no Vim. Alguns exemplos incluem:
Ainda há diversos outros comandos úteis como os de recolher/expandir (folding) e outros recursos interessantes que comentarei assim que tiver mais tempo disponível.
Já falei sobre vários comandos e sugiro que se comece tentando aprender aqueles que vocês mais utilizem, como Snippets, busca e substituição simples, abertura rápida de arquivos, uso de abas e navegação entre buffers. Para os que trabalham com HTML, também recomendo estudar os comandos do plugin “surround”, que são especialmente úteis para trabalhar com tags.
Como uma última nota, este artigo foi escrito no Vim no formato Markdown. Muitos dos exemplos envolvem tags e para escapá-las no documento, utilizei o comando ‘:%HTMLSpecialChars’ de um plugin incluído recentemente: htmlspecialchars.
Bom proveito!
Eu costumo dizer que as pessoas que participaram das discussões sobre como deveria ser a linguagem Java fumaram um baseado antes de fazê-lo.
Na época, a linguagem mais comum era C++, que já existia há 10 anos e que, por sua vez, surgiu mais de 10 anos após a linguagem C, inspirada pela última, introduzindo conceitos mais modernos de orientação a objeto, entre outros. Tanto C e C++ foram projetadas por uma única pessoa, diferentemente da linguagem Java, a qual foi projetada por um grupo de pessoas, que na minha opinião iniciaram a reunião com um baseado, que é o que justificaria as decisões erradas que relato a seguir.
O maior problema com C e C++ é que o desenvolvedor precisava preocupar-se com o gerenciamento de memória, sendo forçado a escrever instruções para alocar e desalocar memória. Como todo ser humano, às vezes os desenvolvedores esqueciam-se de desalocar alguma memória e, ao longo do tempo, o programa que sofria de fuga (leak) de memória terminaria por consumir toda a memória disponível até que fosse terminado pelo sistema operacional. Este era um dos sintomas, mas havia outros.
O fato é que na época em que as linguagens C e C++ foram projetadas, os recursos de memória eram escassos e os desenvolvedores realmente precisavam ter controle sobre o gerenciamento de memória em muitos casos. Além disso, gerenciar memória automaticamente não é uma tarefa trivial.
Outro problema de programação enfrentado na época estava relacionado com a portabilidade do código. Escrever código portável não era trivial. Para escrever programas portáveis, foram criadas ferramentas como autotools e bibliotecas multi-plataforma, com várias condicionais para utilizar código diferente para abrir uma janela gráfica, por exemplo, dependendo do sistema operacional alvo da compilação. Esta técnica ainda é utilizada em programas como KDE, Opera, Google Chrome, Mozilla Firefox e Thunderbird entre vários outros. No entanto, não é fácil garantir que todos os recursos estarão presentes em todos os sistemas operacionais com esta técnica, nem que o resultado visual será exatamente o mesmo. Alguns não consideram isso um problema, mas outros sim. Não vou discutir essa questão aqui, mas os defensores das duas abordagens (abordarei a outra logo a seguir) têm argumentos válidos e não há uma solução certa para esse problema.
Quando a linguagem Java foi idealizada, seu principal objetivo era facilitar o desenvolvimento de programas portáveis, que se comportassem exatamente da mesma forma em qualquer sistema operacional. Isto significava que o desenvolvedor não teria que se preocupar tanto com gerenciamento de memória nem com questões específicas de sistema operacional. O objetivo é completamente válido e algumas decisões foram acertadas, como o uso de uma máquina virtual. A idéia de utilizar uma máquina virtual também foi adotada pelo framework mais novo Microsoft .NET, recentemente. E de fato, a linguagem recém criada pela Microsoft C# é mais parecida com Java que com C ou C++. Talvez devesse chamar-se Java# ou Cb…
Uma máquina virtual é uma especificação de um formato único de armazenamento de instruções pré-compiladas que serão interpretadas de modo ligeiramente diferentes dependendo da arquitetura do processador e do sistema operacional. Desta forma, a distribuição binária de programas é extremamente facilitada porque os mesmos arquivos binários funcionarão da mesma forma em todos os sistemas em que houver uma máquina virtual disponível. A distribuição binária de programas compilados em C ou C++ sempre foi um pânico e resolver este problema era outro objetivo do projeto da linguagem Java.
Para minimizar o problema de fugas de memória, Java delegou a função de desalocar a memória para a máquina virtual, implementando coletores de lixo (garbage collector). O problema de fuga de memória continua existindo, mas é minimizado consideravelmente, uma vez que, ao menos as regiões de memória que não são mais referenciadas podem ser liberadas pelo coletor de memória sem intervenção do desenvolvedor.
Naquela época, boa parte do desenvolvimento era concentrado em aplicações gráficas, enquanto hoje a maior parte está concentrada em aplicações web. Por isso, Java provia uma biblioteca gráfica junto com a máquina virtual, que tinha como objetivo manter a apresentação visual exatamente da mesma forma em todos os sistemas operacionais. Isto significava que Java não reutilizaria as bibliotecas gráficas que acompanhavam cada sistema operacional e utilizariam apenas uma pequena API que lhe permitisse desenhar pixels. Todo o resto seria implementado do zero. Esta decisão lhe custou uma máquina virtual muito grande para distribuir. No entanto, esta decisão incomodava muitos usuários pelo desconforto visual, já que suas aplicações Java eram visualmente diferentes das janelas dos outros programas que usavam. Mais tarde, outras implementações para solucionar esse problema surgiram, mas o tamanho da máquina virtual só foi aumentando.
Toda linguagem é projetada de acordo com sua intenção. Java não estava disposta a abrir mão de desempenho mais que o necessário e, portanto, não poderia ser tão dinâmica quanto outras linguagens contemporâneas como Ruby ou Python. No entanto, os motivos que inspiraram a criação da linguagem Java não justificam seu projeto ruim. Seguem algumas deficiências da linguagem, que não encontro explicação razoável.
Em C++, era possível declarar um método ou função desta forma:
1 | void Metodo(int argumentoObrigatorio, bool opcional1 = false, int opcional2 = 0); |
Em Java, houve uma regressão. Para obter o mesmo efeito, é necessário implementar em uma classe algo como:
1 | void Metodo(int argumentoObrigatorio, boolean opcional1, int opcional2) { |
2 | // implementação real |
3 | } |
4 | |
5 | void Metodo(int argumentoObrigatorio, boolean opcional1) { |
6 | Metodo(argumentoObrigatorio, opcional1, 0); |
7 | } |
8 | |
9 | void Metodo(int argumentoObrigatorio) { |
10 | Metodo(argumentoObrigatorio, false); |
11 | } |
Esta regressão simplesmente não faz o menor sentido!
A seguinte interface não pode ser definida em Java:
1 | interface UmaInterface { |
2 | static void metodoEstatico(); |
3 | } |
Simplesmente não existe lógica para essa proibição. Isto é consequência, na verdade, de outro problema:
Uma vez que todos os métodos de uma interface são implicitamente abstratos, como não é permitido definir um método estático como abstrato, não é possível definir um método estático em uma interface. Mas não faz sentido impedir que métodos estáticos sejam abstratos.
Em C++, é possível sobrescrever diversos operadores para qualquer classe e isto realmente facilita a programação de uma série de casos de uso.
Este problema está relacionado ao anterior. Seria muito melhor comparar Strings com a == “string”, em vez de “string”.equals(a). O fato do resultado ser diferente é mais perigoso, porque a == “string” é permitido entre Strings, mas o resultado é diferente visto que o conteúdo da string não é verificado. Raramente alguém iria querer comparar se as referências para a string são a mesma…
Trabalhar com datas em Java é um pânico. E este é apenas um exemplo. A quantidade de métodos depreciados é também muito alta e permanecem depreciados por muito tempo. Outro exemplo de API mal pensada é a ausência de um método join na classe String, enquanto existe um método split. Simplesmente não faz sentido.
Com relação à API de datas, imagine como se escreveria um código para verificar se dois objetos tipo Date equivalem ao mesmo dia, descartando a informação de horas, ou o que seria necessário fazer para obter um objeto que contenha apenas a porção da data, ajustando a parte das horas para 0. Em várias linguagens, existem tipos separados para representar data e hora, data ou somente hora, e normalmente existem operadores de soma e subtração que funcionam como esperado entre esses tipos. Lidar com datas no Java é um pânico devido à API mal projetada e devido a ausência de flexibilidade para sobrescrita de operadores, como já foi comentado.
Closures permitem construção de código muito mais expressivo, facilitando a manutenção. A utilização de construções tipo closure não impacta em si no desempenho do resultado produzido, mas permite simplificar bastante a escrita e manutenção de código e dariam nova vida à linguagem Java.
Em C++, é possível definir tipos assim:
1 | class Classe { |
2 | typedef Resultado map<string, void *>; |
3 | Resultado Metodo() { |
4 | return new Resultado(); |
5 | } |
6 | |
7 | vector<Resultado> OutroMetodo() { |
8 | return new vector<Resultado>(); |
9 | } |
10 | } |
Normalmente, haveria uma série de outros métodos utilizando Resultado. Se quisermos alterar a definição de Resultado para map
Em Ruby e Object Pascal, por exemplo, há o conceito de propriedades de objeto embutido na linguagem:
Ruby:
1 | class Classe |
2 | attr_accessor a, b |
3 | attr_reader r |
4 | attr_writer w |
5 | |
6 | def b=(valor) |
7 | puts 'Valor b atribuído' |
8 | @b = valor |
9 | end |
10 | end |
Equivalente em Java:
1 | class Classe { |
2 | private int a, b, r, w; |
3 | |
4 | void setA(int a) { |
5 | this.a = a; |
6 | } |
7 | |
8 | int getA() { |
9 | return a; |
10 | } |
11 | |
12 | void setB(int b) { |
13 | System.out.println("Valor b atribuído"); |
14 | this.b = b; |
15 | } |
16 | |
17 | int getB() { |
18 | return b; |
19 | } |
20 | |
21 | int getR() { |
22 | return r; |
23 | } |
24 | |
25 | void setW(int w) { |
26 | this.w = w; |
27 | } |
28 | } |
Seria muito melhor se fosse possível utilizar expressões para construir listas, mapas e expressões regulares de modo mais direto. Veja esses exemplos em Ruby 1.9:
1 | if chama_metodo([elementos, de, uma, lista, recem, criada], |
2 | {usando: 'mapa', diretamente: true}) =~ /resultado(.*)esperado/ |
3 | puts $~[1] |
4 | end |
Os próximos tópicos são realmente questão de gosto pessoal e não é possível culpar os projetistas da linguagem Java pelo projeto. São opiniões pessoais minhas e que reconheço que outros possam discordar com argumentos válidos.
Em muitos casos, diretivas de pré-processamento na etapa de compilação podem ser úteis. As macros do C++ podem permitir estilos de código ruins, mas também podem facilitar bastante alguns trechos de código. Veja um exemplo:
1 | #define verifica(parametro) if (parametro == null || parametro->invalido()) return 0 |
2 | |
3 | int funcao(Classe *p1, Classe *p2, Classe *p3) { |
4 | verifica(p1); |
5 | verifica(p2); |
6 | verifica(p3); |
7 | // Resto da função aqui... |
8 | } |
9 |
Esse é um exemplo simplista, mas certamente há casos em que o uso de macros é ainda mais interessante.
Eu prefiro muito mais o estilo Ruby (e a maioria das linguagens) do que o estilo Java para este caso:
Ruby, Perl, Javascript, etc:
1 | if (obj && obj.metodo()) facaAlgo(); |
Java:
1 | if (obj != null && obj.metodo()) facaAlgo(); |
Nem só de erros consiste a linguagem Java. Conceitos de máquina virtual e gerenciamento automático de memória com coleta de lixo, aplicados na arquitetura Java, facilitam a distribuição e minimizam certos tipos de erros, como fugas de memória. De fato, a máquina virtual Java tem-se tornado bem sólida e eficiente ao longo dos anos, desde que foi criada. Por isso, muitas linguagens dinâmicas estão disponíveis para a máquina virtual Java (JVM), como JRuby, Jython e outras linguagens surgiram especificamente para integrar com a JVM, com Groovy e Scala.
Isto significa que é possível evoluir um sistema atual que executa na JVM com uma linguagem mais dinâmica ou mais bem projetada, sem precisar desfazer-se da base de código atual. A JVM é bastante sólida e tem bom desempenho, além de ser bem testada e é um excelente ambiente para execução de programas escritos em linguagens mais interessantes como JRuby, Groovy, Jython ou Scala, por exemplo, dependendo de muitos fatores, os quais não tratarei nesse artigo, mas incluem desempenho, disponibilidade de bibliotecas e frameworks e finalidade da aplicação.
Até mesmo a linguagem Java não seria um caso perdido, caso ela passasse por uma reformulação, envolvendo adição dos recursos citados neste artigo e uma alteração em sua API.
Sempre que eu preciso estudar sobre algo relacionado a Java deparo-me com uma das seguintes possibilidades:
Há cerca de um ano, tive meu primeiro contato com Java trabalhando em projetos reais. Na época, a equipe com que trabalhava utilizava projetos criados com o Netbeans e uma série de procedimentos para lidar com o gerenciamento de dependências entre os projetos. Nos últimos meses a equipe vem aplicando uma série de inovações, como a mudança do CVS para o Git, testando metodologias ágeis de gerenciamento de projeto, como SCRUM e XP, migrando os projetos para a estrutura do Maven, entre outras.
A motivação para a migração para o Maven era que o gerenciamento de dependências seria bastante simplificado, mas por muito tempo essa decisão foi adiada porque não havia uma documentação concisa e rápida sobre como funcionava o Maven na prática. Era necessário ler muito material sobre o Maven, o que demandava tempo. Assim que dispomos deste tempo, resolvemos migrar os projetos para o Maven, mais ou menos, na mesma época em que migramos o sistema de controle de versão para o Git. A seguir, relato como foi esse processo (da parte do Maven, apenas), na esperança que possa auxiliar outros na mesma situação. Adianto que o esforço se paga rapidamente!
Maven é uma ferramenta que realiza várias tarefas:
Nossa primeira decisão foi criar um proxy para o Maven, e para isso utilizamos o Nexus, cuja instalação é trivial, bastando seguir as instruções do projeto, que são exceção à documentação típica que citei no início do artigo. Este passo não é obrigatório e não será discutido neste artigo, mas possui várias vantagens ao se trabalhar em uma equipe de desenvolvedores.
Todo projeto Maven possui um arquivo pom.xml que relaciona as dependências do projeto bem como a estrutura como o projeto estará organizado. É possível herdar configurações de outro pom.xml, favorecendo a modularidade e não repetição de código (princípio DRY). Outro princípio utilizado é o Convenção Sobre Configuração. Isto significa que apenas alguns parâmetros de configuração são essenciais. Para os outros, quando não especificados, certas convenções serão assumidas.
Por exemplo, um projeto Maven típico terá a seguinte estrutura de arquivos:
projeto -pom.xml -src -main -java -br -com... -test -java -unit_test.java -resources -META-INF -logo.png
Ou seja, o projeto será composto pelo pom.xml, um diretório src, onde ficarão os fontes (dentro do sub-diretório java se os fontes estiverem em Java), um diretório test onde estarão os testes automatizados da aplicação e um diretório resources, onde ficarão os recursos do projeto. Podem ser imagens obtidas com getResource(“/logo.png”), por exemplo, ou folhas de estilo e javascripts que farão parte de uma aplicação web.
Estes projetos são referidos como artefatos, na terminologia do Maven. Após instalar o Maven, execute a linha abaixo para gerar um artefato vazio, de modo que possamos explorá-lo:
1 | mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false |
Produtividade é basicamente a relação entre resultado útil e o tempo necessário para realizá-lo.
Frequentemente, eu leio artigos que clamam que a linguagem X torna o trabalho mais produtivo que a linguagem Y por n motivos, normalmente ilustrados com trechos de código para validar sua tese. A maior parte destes artigos foca no argumento que é possível escrever um mesmo resultado com menos linhas de código em sua linguagem preferida. Eu comentarei mais sobre os outros argumentos utilizados em outros artigos, mas focarei na relevância da quantidade de código escrito quando se fala em produtividade.
A maior parte dos textos que li sobre comparações entre linguagens/frameworks mostram como é possível escrever menos código em uma linguagem ou framework e concluem que a linguagem/framework X é mais produtiva que Y sem explicar a relação entre quantidade de código escrito e produtividade.
Frequentemente eu invisto muito mais tempo para escrever um código menor do que eu levaria para obter o mesmo resultado com mais código. Os motivos são gosto pessoal (gosto de ler soluções elegantes e código sintético) e, principalmente, preocupação com produtividade a longo prazo. Obviamente, a curto prazo, nesses casos, a produtividade é maior se eu escrever mais código, o que mostra que a produtividade é variável ao longo do tempo de vida de um projeto.
Quanto mais um projeto cresce, mais o tamanho e a consistência de seu código tornam-se mais relevante. Existem fatores mais relevantes para a produtividade de um sistema do que o tamanho do código em si: documentação, cobertura por testes automatizados, modularidade, comunicação eficiente entre desenvolvedores e clientes, para citar alguns. Isto não quer dizer que reduzir a quantidade de código não aumente a produtividade. De fato pode aumentar consideravelmente, mas é necessário explicar por quê.
Aqueles que já desenvolveram com a IDE Delphi lembrar-se-ão que muito código era adicionado e removido automaticamente pelo IDE e que estes códigos não influenciavam absolutamente na medição de produtividade. De fato, o IDE era o ponto forte do Delphi, não a linguagem. Era raro encontrar material sobre diversos tópicos nos tutoriais e livros disponíveis sobre o Delphi, incluindo: documentação, internacionalização, modularização, distribuição binária e integração com sistemas de controle de versão. Certamente havia material disponível sobre estes assuntos, mas pouquíssimos desenvolvedores em Delphi estudaram esses tópicos.
A maior parte sentia-se produtivo pela facilidade de criar um formulário, adicionar controles e botões e tratar rapidamente os diversos eventos disponíveis para cada controle. De fato, era raro achar uma alternativa que permitia iniciar um projeto Desktop com a mesma velocidade obtida com o Delphi. No entanto, mesmo antes do desenvolvimento para a Web ter-se tornado tão popular, o Delphi já não era muito utilizado em nível mundial. O Delphi foi um fenômeno no Brasil e ainda existem diversos sistemas em uso desenvolvidos em Delphi no país, mas o Brasil foi exceção neste caso.
Aqueles que já tiveram a oportunidade de trabalhar com este IDE lembrar-se-ão como era rápido começar um projeto mas muito complicado de controlar seu crescimento ou dar continuidade em um sistema desenvolvido por outros. Alguns arguirão que isto acontece em qualquer linguagem/IDE/framework, mas isto não é verdade. Eu contribuo com frequência para alguns projetos sem dificuldade em entender o código, mesmo sem a ajuda dos desenvolvedores principais.
Toda linguagem, framework ou IDE trazem consigo toda uma cultura comunitária. Cada comunidade costuma concordar com uma série de premissas. Em Delphi, a cultura geral da comunidade de usuários era entregar mais rápido em vez de com mais qualidade. Isto significava que vários códigos eram copiados de um tratamento de evento para outro em vez de escreverem um código mais modular. Não digo que todos os desenvolvedores pensavam da mesma forma, mas essa era a cultura geral entre os “Delphistas”.
Ou seja, se o objetivo fosse desenvolver um projeto de vida curta, Delphi certamente seria uma excelente escolha caso fosse economicamente viável. Para projetos de vida longa, eu realmente não recomendaria esta abordagem para sistemas Desktop. Outra conclusão é que não se trata apenas do fato de uma tecnologia lhe permitir fazer isso ou aquilo (Delphi permitia modularização e diversas outras boas práticas) mas de como a comunidade que está por traz daquela tecnologia pensa. Na comunidade Ruby, por exemplo, o desenvolvimento orientado a testes automatizados é altamente recomendado e utilizado, com foco tanto em produtividade quanto em qualidade (de código e funcionamento), enquanto a preocupação com desempenho é adiada ao máximo.
Muito já se discutiu no passado sobre as vantagens ou desvantagens do Delphi em relação a outras tecnologias para desenvolvimento em Desktop. Atualmente, o foco está na comparação entre linguagens e frameworks para o desenvolvimento para web, com artigos comparando diversas linguagens (Java, C#, VBScript, Ruby, Python, Perl, C++), frameworks para web (.NET, Struts, JSF, Rails, Django, TurboGears) e frameworks Javascript (jQuery, Prototype, MooTools) para não citar outras tecnologias como Flex, Flash, Java applets, etc.
A maior parte das discussões gira em torno da polêmica Produtividade versus Escalabilidade. Como vocês podem perceber, o assunto é muito amplo e, por isso, resolvi focar apenas na quantidade de código envolvido, deixando os outros fatores para futuros artigos.
Depende, normalmente sim. As perguntas mais interessantes são “em que situações?” e “por que?”. Alguns desenvolvedores só se consideram produtivos com o uso de IDEs. Neste caso, as IDEs são capazes de gerar bastante código, de modo que o programador escreva de fato pouco código. Uma linguagem em que esta situação se aplica é Java. Praticamente todo desenvolvedor Java utiliza um IDE, seja o Netbeans, Eclipse, IntelliJ ou qualquer outro. Eu nunca conheci um desenvolvedor Java que não use um IDE.
Deste exemplo é que vem minha primeira observação: a produtividade em um projeto está minimamente relacionada ao tempo gasto puramente escrevendo código. Isto porque boa parte de um código pode ser gerada por um IDE em menos de 1 segundo e porque o tempo que um desenvolvedor leva para escrever um código é desprezível quando comparado ao tempo em que ele leva pensando na implementação de funcionalidades ou correção de bugs.
Além disso, existem IDEs que não apenas geram códigos clichê (boilerplate), como construtores, Getters e Setters no Java ou C++, mas também esqueletos completos de aplicações funcionais baseados em diagramas UML, por exemplo. Neste caso, em um minuto é possível mostrar uma aplicação completa sendo desenvolvida com um determinado IDE. Certamente é possível dizer que tal IDE é super produtivo se o resultado final desejado for exatamente aquele gerado pelo IDE!
No entanto, na quase totalidade dos sistemas não é isso que acontece. Em sistemas grandes, relevantes para nós (caso contrário você não perderia tempo lendo este artigo imenso), o tempo útil de um projeto é de anos ou décadas. Isto significa que ao longo do projeto, a maior parte do tempo é utilizada com manutenção e evolução do código, além da integração de novos membros à equipe de desenvolvedores, enquanto os membros que iniciaram o projeto abandonam a empresa ou projeto em algum momento.
A maior parte dos programas geradores de código funcionam bem para criar uma aplicação do zero, mas não permitem ou dificultam o caminho inverso, que acontecerá na maior parte do tempo, durante a manutenção do sistema. Geralmente as apresentações dessas ferramentas focam na construção de um aplicativo a partir do nada, pois é justamente a área em que são produtivas, e assim conquistam a maior parte do público leigo ou de não programadores que se espantam de quão rápido uma ferramenta foi capaz de construir uma aplicação inteira.
E normalmente esse é o início de todos os males. Os primeiros desenvolvedores são taxados de produtivos por conseguirem desenvolver protótipos tão rapidamente e ganham prestígio entre os cargos gerenciais e possivelmente alguma promoção onde deixam de desenvolver para coordenar uma equipe de desenvolvedores. A partir desse momento, a ferramenta já não é mais útil pois a aplicação já existe e a maior parte do trabalho está relacionada à manutenção e continuação do programa, o que costuma ficar cada vez mais complicado visto que o código gerado nem sempre é modular ou fácil de entender e manter.
Ou seja, no estado inicial desses projetos, a produtividade será muito elevada, não importando a quantidade de código gerado. Por outro lado, após um ano de desenvolvimento, provavelmente o projeto estará em um nível em que é muito complicado de evoluir ou dar manutenção. Neste estágio, muitas vezes são disponibilizadas atualizações das ferramentas ou novas ferramentas que prometem mais funcionalidade. É tão complicado dar manutenção na base de código existente que diversas equipes reescrevem sistemas completos quando chegam neste ponto.
Assim, vários sistemas foram reescritos de Microsoft ASP para ASP.NET ou C#, vários foram migrados de Java (Struts, JSP, JSF, etc) para Rails e de Microsfot MFC para .NET, sem contar algumas aplicações Desktop que foram reescritas com um foco para a web. Em vários destes casos, a decisão de reescrever todo o sistema melhorou a produtividade geral do projeto, mas em grande parte houve apenas um reinício deste ciclo. Muitos gerentes de projeto justificam que este é um ciclo normal e calculam o tempo de vida de um projeto em 4 anos, quando muito.
Já ouvi algumas pessoas insinuando que Ruby é uma linguagem mais moderna e dinâmica que Java por ser mais nova. Que elas não podem ser comparadas porque Java é muito mais antiga e os recursos disponíveis na época em que foram criadas não permitem uma comparação justa. O fato é que não poderia ser mais justo visto que ambas as linguagens surgiram na mesma época, na verdade. Ou seja, uma linguagem, assim como um projeto, quando bem projetado e evoluído pode durar facilmente uma década, sem afetar sua manutenabilidade. Testes automatizados contribuem enormemente para o sucesso destes projetos.
Existem vários projetos de código aberto que continuam evoluindo após vários anos de existência. A comunidade de software livre costuma ter um padrão superior para a qualidade de código exigida, quando comparada às empresas em geral, especialmente porque a base de desenvolvedores envolvidos também costuma ser maior e por isso é muito importante que o código tenha qualidade, documentação, testes, suporte a internacionalização, entre outras qualidades. Vários deles passaram por refatoração de boa parte do código, ao longo do tempo, e alguns foram até mesmo completamente re-escritos, como a migração do KDE 3 para o KDE 4. Mas de um modo geral, a duração de um software livre costuma ser muito superior a de softwares particulares.
Quanto mais tempo dura um projeto, maior costuma ser a base de código e é justamente nesse momento em que a quantidade de código mais importa. A quantidade de código consome muito mais tempo de entendimento e mudança do que de criação do código propriamente. Quando o código é pequeno, modular, auto-contido e conciso, o tempo necessário para seu entendimento e evolução é consideravelmente menor.
Para exemplificar, imagine que seja necessário implementar um algoritmo que dado n vetores de inteiros, pretenda-se filtrar os elementos da interseção destes vetores, gravando em um arquivo estes elementos separados por vírgula.
Seguem duas implementações, uma em Java e outra em Ruby:
Java:
1 | /* suprimindo as declarações de package e imports gerados pelas IDEs e que não costumam ser |
2 | lidas por desenvolvedores Java, bem como interfaces e extends normalmente utilizados em |
3 | projetos Java típicos e incluindo as bibliotecas Apache Commons para suprir as deficiências |
4 | da API padrão do Java. Se você acredita que é possível tornar este código menor, por favor |
5 | deixe um comentário ou envie-me um e-mail */ |
6 | public class App { |
7 | static void grava_intersecao(String nome_arquivo, List... vetores) { |
8 | List intersecao = vetores[0]; |
9 | for (int i=1; i < vetores.length; i++) intersecao.retainAll(vetores[i]); |
10 | try { FileUtils.writeStringToFile(new File(nome_arquivo), StringUtils.join(intersecao, ','));} catch (IOException e) {} |
11 | } |
12 | public static void main(String args[]) { |
13 | List imparesDe1A20 = new ArrayList<Integer>(); |
14 | for (int i=1; i<=19; i+=2) imparesDe1A20.add(i); |
15 | grava_intersecao("arquivo.txt", Arrays.asList(1,6,9), imparesDe1A20, Arrays.asList(9,87,56)); |
16 | } |
17 | } |
Ruby:
1 | def grava_intersecao nome_arquivo, *vetores |
2 | File.open(nome_arquivo, 'w'){|f| f.write vetores.inject{|a,b| a & b}.join(',')} |
3 | end |
4 | |
5 | grava_intersecao 'arquivo.txt', [1,6,9], (1..20).step(2).to_a, [9, 87, 56] |
O tempo que eu levo para escrever ambas as soluções é muito pequeno, independentemente da quantidade de linhas de código (que seriam muito maiores normalmente, pois os desenvolvedores costumam usar a IDE para identar o código). No entanto, eu levei muito mais tempo para desenvolver a solução em Java pois tive que pesquisar diversas APIs… Comparando o número de caracteres úteis entre as duas implementações, o código em Ruby equivale a cerca de 40% do código em Java.
Qual destes códigos seria mais rapidamente compreendidos e seria mais fácil de manter por um novo desenvolvedor que estivesse sendo integrado a um projeto?
Tenha em mente que em um projeto grande este trecho seria apenas uma agulha em um palheiro. Portanto multiplique diversas vezes o tempo de entendimento destes trechos para ter uma idéia do tempo necessário para integrar um novo desenvolvedor a um projeto em uma linguagem mais expressiva quando comparado a um projeto desenvolvido em uma linguagem menos expressiva (leia-se: quantidade maior de código).
Curiosidade: embora não seja o foco deste artigo, deixe-me aproveitar para comentar um pouco sobre um tópico que merece um artigo à parte: inconsistência. O código em Java acima não funcionará por um detalhe sutil. Seria muito complicado para um desenvolvedor Java, mesmo um experiente, saber prontamente por que este código não funcionará. Eu mesmo só percebi quanto tentei executá-lo e precisei ler a documentação para entender. Mesmo com a documentação, ela não é clara o suficiente para tirar conclusões definitivas. Seria preciso testar de qualquer forma. Uma alteração simples faria com que o código acima funcionasse:
1 | /* a ordem dos parâmetros foi ligeiramente alterada */ |
2 | grava_intersecao("arquivo.txt", imparesDe1A20, Arrays.asList(1,6,9), Arrays.asList(9,87,56)); |
A documentação de retainAll avisa que é possível que seja gerada uma exceção UnsupportedOperationException se o método não for suportado pela lista. No entanto, a documentação de Arrays.asList não diz se a lista retornada suporta ou não esta operação. Também não achei menção ao suporte desta operação na documentação de ArrayList. Foi necessário testar em ambos os casos e, mesmo assim, como eu poderia ter certeza se funcionaria com outra implementação da linguagem Java do que a que estou usando?
Aproveito para comentar sobre outro pensamento comum que é incorreto: “em uma linguagem compilada tem-se mais garantia que um código irá funcionar quando o programa é compilado, comparado a um programa interpretado sem fase de compilação” ou “uma linguagem de tipagem estática tem menos chance de gerar uma exceção do que uma linguagem com tipagem dinâmica”. Este exemplo mostra que mesmo que o código em Java compile, haverá um erro em tempo de execução dependendo do tipo de lista que será passada como primeiro parâmetro. Por isso é importante escrever testes automatizados, independentemente do tipo de linguagem. A melhor forma de corrigir o código Java acima provavelmente seria:
1 | //List intersecao = vetores[0]; |
2 | List intersecao = new ArrayList(vetores[0]); |
Voltando ao tópico principal, quando se diz que uma linguagem permite escrever um mesmo sistema com menos código, significa que a linguagem é mais expressiva, e que é mais fácil entender e evoluir um código em uma linguagem mais expressiva. Concluindo: a quantidade de código escrito pouco afeta a produtividade com relação ao tempo gasto para escrever o código quando comparado ao enorme ganho no tempo necessário para entendê-lo, mantê-lo e evoluí-lo.
Em 2009, eu escrevi um artigo para a quarta edição da Rails Magazine mostrando uma alternativa para geração de PDFs a partir de modelos em ODF, o qual pode ser gerado através de um editor de texto comum como o BrOffice.org ou Microsoft Office (após convertido para o formato ODF).
O artigo pode ser lido na íntegra através da versão eletrônica disponibilizada pela revista gratuitamente. O código de uma aplicação em Rails exemplificando o processo foi disponibilizado pela revista e pode ser encontrado no Github.
Infelizmente, não tenho como hospedar um sistema funcional devido a limitações da minha conta no Heroku, mas basta seguir as instruções no artigo para replicar o sistema em seu ambiente de desenvolvimento ou produção.
Quaisquer dúvidas, não hesitem em deixar um comentário ou enviar-me um e-mail.
Article content here.
Para inaugurar este site, resolvi escrever meu primeiro artigo sobre os motivos que me levaram a finalmente escrever um site, como ele está estruturado em termos técnicos e por que eu tomei certas decisões ao desenvolvê-lo.
Há muitos anos eu vinha pensando em escrever meu site. Havia muitos assuntos sobre os quais eu tinha interesse em escrever e discutir, mas por muito tempo eu realmente não tive tempo. Muita correria: graduação, mestrado, emprego, casamento. Quando eu tinha algum tempo à noite eu estava extremamente cansado para preparar um site.
Durante esse tempo, eu cheguei a pensar em utilizar uma estrutura pronta como o Blogger, Wordpress e outros, mas eu simplesmente não gostava da ideia de perder o controle sobre meus artigos. Embora eu pudesse personalizar o Wordpress, ele é escrito em PHP, então, resumindo: não, obrigado. Eu gostaria que meu site fosse exatamente como eu planejasse e essas ferramentas não me permitiriam controle total sobre o site e eu imaginava que futuramente daria muito trabalho para migrar os artigos para uma nova estrutura de site.
Quando passei a ter mais tempo disponível durante as noites, voltei a pensar em construir o site, do meu jeito, em Rails. Mas eu esbarrava sempre no mesmo problema. Eu não estava disposto a investir dinheiro mensalmente em um serviço de hospedagem para disponibilizar um site sobre o qual eu não teria qualquer retorno financeiro. Tampouco eu encontrava um serviço de hospedagem gratuito que suportasse Rails e nem estava disposto a desenvolver o site em outra tecnologia.
Recentemente, vários dos blogs que acompanho nos meus feeds comentaram sobre o Toto. Tanto falaram que resolvi ler mais profundamente sobre o Toto e fazer uns testes com ele. Toto é um sistema para construção de blogs escrito em torno de 300 linhas de código em Ruby, diretamente sobre o micro-framework web Rack.
Toto foi o motivo que culminou em minha decisão de finalmente levar à frente a ideia da construção deste site. Estas foram as principais ideias que inspiraram o projeto deste site:
Minha primeira ideia foi realizar exatamente o procedimento recomendado pelo Toto. Na medida em que ia elaborando o site sobre o Toto, eu esbarrei em alguns obstáculos:
O código do Toto é tão simples de entender que é trivial adaptá-lo para uma aplicação Rails e acomodá-lo para atender minhas expectativas. E foi exatamente o que fiz. Adicionei suporte à internacionalização e permiti uma organização dos artigos em pastas, como meta-informações sobre os tópicos que representam.
No fundo, as principais ideias deste site foram extraídas do Toto, projeto ao qual sou bastante grato. Agradeço ao seu criador Alexis Sellier, pela inspiração que resultou neste site.
Até o momento, nenhum banco de dados está sendo utilizado e as estatísticas de acesso estão sendo gerenciadas pelo Google Analytics. As imagens estão sendo hospedadas pelo Amazon S3 e para destacar os códigos está sendo utilizado o Ultraviolet. RDiscount está sendo utilizado para processar os textos em Markdown.
Dada a inauguração, espero que gostem dos próximos artigos.
Article content here.