Quantcast
Channel: AkitaOnRails.com
Viewing all 481 articles
Browse latest View live

[Off-Topic] Matemática, Trolls, Haters e Discussões de Internet

$
0
0

Em tempos de redes sociais e discussões abertas em timelines, foruns, etc, já nos acostumamos com o fato de nunca existir consenso em nada. E em tempos de Twitter onde 140 caracteres definem o que vamos dizer, soltar algo como "X funciona", "Y não funciona", "concordo com A" ou "discordo de B"é garantia de um longo e inacabável flame war.

E o que eu posso fazer, também sou culpado, viciado em argumentar tanto em assuntos sérios quanto assuntos irrelevantes - faz parte do entretenimento, eu acho -, mas devo concordar que algumas vezes falta paciência mesmo. Quem assistiu minhas palestras mais recentes deve lembrar de uma parte que digo o seguinte: que as pessoas buscam "validação" de suas idéias e argumentos, parece que quanto mais gente concorda, mais a idéia é válida. O tal processo "democrático" onde 50% + 1 garante vitória.

Existem vários problemas nisso. Já discuti parte desse tema no artigo Mea Culpa: Organizações Democráticas não Funcionam. Embora eu não seja um matemático formado e atuante, minha fundação é de matemática. Uma das coisas felizes que lembro do meu tempo no Instituto de Matemática e Estatística da USP foi justamente a pesada fundação matemática a que fui submetido. Até hoje trago o que aprendi anos atrás para meu dia a dia.

Leis são Leis

A primeira grande coisa que eu aprendi é o seguinte: dado um conjunto de axiomas, dado que determinado teorema está comprovado, posso utilizar essa ferramenta com a garantia de que ele sempre vai funcionar dentro das premissas estipuladas.

Portanto, dado que números naturais existem, dado x pertencente aos naturais, dado as propriedades algebraicas dos naturais, sei que y = x + x e que y estará nos naturais. Dado tudo isso, posso dizer com toda certeza que 2 + 2é 4 e sempre será 4.

Isso é importante porque eu não preciso de "validação" de maioria para saber disso. Não importa se alguém propor uma votação e 1 milhão de pessoas disserem que 2 + 2 é 5. Quantidade de opiniões não desfaz a Álgebra. Certas áreas do conhecimento científico já foram totalmente comprovadas, não preciso de consenso de "opiniões".

Maioria, Autoridade, não significa nada dentro desse contexto.

Isso me dá diversas certezas: significa que existe todo um corpo de conhecimento científico nas mais diferentes áreas que vai de matemática, à física, à biologia, à psicologia, à sociologia, à medicina, etc que são confiáveis e estão à nossa disposição para serem utilizadas. E sua utilização não depende de consenso, não depende de votação, não depende de "validação" de nenhum grupo ou conjunto de pessoas.

Me dá a certeza também que só porque eu não sei alguma coisa, não significa que alguém já não saiba, e aumenta minha estima pela pesquisa adequada desse corpo de conhecimento. O erro é a preguiça de fazer essa pesquisa e assumir que porque alguma maioria disse alguma coisa, ou porque alguém com aparente autoridade disse alguma coisa, isso "deve ser verdade".

Pra mim, por definição, tudo está errado até que seja rigorosamente comprovado que é verdade, pelos mesmos métodos que garantiram esse corpo de conhecimento científico.

Problemas de Primeiro Mundo

No mundo da auto-ajuda existem milhares de "fórmulas", ou "receitas". Receitas para emagrecer, receitas para ter sucesso, receitas para ser feliz e assim por diante. É uma enorme indústria que pelo menos até 2010 movimentava cerca de USD 11 bilhões. Ela não vai parar e deve crescer.

Nós nos movemos na pirâmide de Maslow. A geração de nossos pais e nossos avós ainda tinham que se preocupar com a sobrevivência mais básica: alimentação, saúde. Nesta nova geração uma parcela enorme da população vive muito melhor, onde essa base já não é a maior prioridade. Mas não tem problema, o ser humano sempre vai procurar problemas para se preocupar, os famosos "problemas de primeiro mundo" - algumas vezes parece que somos uma espécie feita para encontrar problemas até onde eles não existem.

É por isso que o mercado de auto-ajuda cresce tanto, por isso que problemas que parece que simplesmente não existiam alguns séculos atrás agora parecem tão maiores: depressão, stress. E onde medicamentos que nunca fizeram tanta falta 2 ou 3 séculos atrás agora são fabricados em toneladas: Zoloft, Prozac, Paxil, Celera.

Para constar, não estou menosprezando esses problemas, uma parcela enorme é bastante real e ainda bem que a medicina moderna possui esses instrumentos para tratar. Porém é inegável que uma grande parcela faça muito mal uso dessa circunstância já que, diferente de uma perna quebrada, não há evidência física desse tipo de problema.

Cito isso porque já faz parte da nossa cultura atual assumir que esse tipo de patologia é uma coisa comum, a cada nova geração, parece que todo mundo é feito de cristal e qualquer pequena disrupção já quebra. A impressão que dá é que se essa geração fosse magicamente transportada para o século XVII ou antes, ninguém sobreviveria.

O efeito da Auto-Ajuda

Isso tudo dito, vem a parte que mais me incomoda: as fórmulas mágicas, balas de prata. Dentro do contexto do mercado de auto-ajuda até que eu não me incomodo tanto. Afinal, que mal tem alguém ler "Quem Mexeu no Meu Queijo" para se entreter?

O problema é quando pseudo-ciência é tomado como ciência real.

É o que eu chamo de "porque funcionou pra X deve funcionar pra Y", ou o que mais gente já me ouviu repetir ad nauseaum que "correlação não é causalidade".

Toda auto-ajuda são uma ou mais fórmulas, devidamente embrulhadas em pacotes de muito marketing e vendidas como soluções universais.

"Basta pensar positivo."

Por si, afirmações como essa são inofensivas, mas elas rapidamente se desenrolam em proporções bíblicas.

Qual o problema de fórmulas? Novamente me remeto à minha fundação matemática. Uma fórmula matemática, sozinha, não serve para nada, ela é indefinida, incompleta. Para ser definida de forma completa ela precisa de pelo menos mais dois elementos: um Domínio e uma Imagem. Por exemplo, veja a seguinte fórmula:

1
f(x) = 1/x

Imediatamente alguém poderia dizer "mas esta fórmula está errada, pois se x for zero a conta não funciona." E de fato, é uma boa observação, mas ela parte de uma premissa: que quem disse essa frase está considerando que x pode ser qualquer número que existe.

Porém, se eu definir corretamente e dizer:

1
dado x pertencente ao domínio dos números reais, e dado x diferente de zero, f(x) = 1/x, resultará na imagem dos números reais

Agora esta fórmula, no campo da Álgebra, está bem definida e é incontestável. Notem o que eu fiz com este simples exemplo: dei uma fórmula incompleta e imediatamente gerei uma discussão sobre ela. É exatamente isso que acontece em toda discussão em redes sociais e temas de auto-ajuda.

Repetindo: toda auto-ajuda são uma ou mais fórmulas, que não definem o domínio e nem a imagem dessa fórmula. Cada pessoa que lê essa fórmula incompleta, tenta completá-la com seu próprio domínio "para mim funciona" ou "para mim não funciona" e acabamos de encontrar a origem mais simples de todo flame war, todo hater, todo troll da internet.

Por outro lado também encontramos a primeira forma de distinguir uma fórmula inválida de uma "possivelmente" válida. Com a inválida você vai ter uma enorme dificuldade de definir o domínio. Partimos sempre do domínio maior, que funciona para todo mundo em todos os cenários. Imediatamente a fórmula quebra, agora começamos a reduzir esse domínio e, invariavelmente, ela vai se reduzir até chegar a um único caso que alguma vez funcionou.

Sistemas Complexos, Biografias e a falácia das "evidências"

E normalmente as fórmulas se reduzem realmente a um único indivíduo, esse é o caso de toda Biografia. A sequência de eventos que determinado indivíduo passou somente ele passou, e elas não se repetem.

Eu falo sobre sistemas complexos faz muito tempo, desde pelo menos 2009 esse assunto chama minha atenção. Já incorri no erro de derivar teorias sobre esse conceito mas devo admitir que até agora não fui bem sucedido, e na verdade todo o campo da sociologia e estudo de redes ainda está na infância desse tipo de conhecimento. Pouco se tem em termos de postulados e leis. Portanto interprete esse tipo de assunto com muito mais cuidado que o normal. A física quântica também já foi indevidamente explorada no mundo da auto-ajuda, veja calamidades horrendas como "What the Bleep do We Know" para ver como transformar um assunto sério numa piada (que muitos acham que é sério).

Correndo o risco de ser muito superficial vou resumir a parte que interessa desta forma: no mundo real somos todos parte de um sistema complexo adaptativo. Uma ação que eu tomo pode desencadear diversas outras ações que, por sua vez, vão cada uma desencadear outras diversas ações, e muitas dessas ações podem interagir entre si, criando uma rede de incalculáveis nós e conexões. É praticamente impossível determinar o final de uma cadeia de eventos em rede com inúmeras variáveis. Digo "praticamente impossível" porque ainda não sabemos como determinar todas as variáveis da equação e, para todos os efeitos práticos, isso torna qualquer coisa impossível de prever.

Claro, dado alguns limites de espaço, tempo, margens de erro, sabemos prever muitas coisas o suficiente para mandar foguetes a Júpiter com precisão. Por outro lado existem muitas coisas cujas margens de erro ainda são difíceis de controlar, a própria meteorologia sendo um exemplo. É como surge a idéia de que uma borboleta bate as asas na China e um maremoto atinge o Rio de Janeiro. Essa frase não significa que a borboleta causou o maremoto, mas sim que ela foi uma condição inicial de uma cadeia intricada de eventos que por acaso resultou no maremoto.

Agora, dado um certo resultado, em muitos casos podemos olhar para trás, para o passado, e rastrear a cadeia de eventos até chegar às condições iniciais. Se alguém já assistiu CSI ou algum programa de detetives sabe que podemos coletar evidências e usar de dedução para conectar eventos até chegar a um ponto de origem. É assim que todas as "fórmulas" são criadas. A cadeia de eventos já aconteceu, ela não vai mais mudar. Dentro do nosso Universo, o passado é imutável porque o tempo só tem uma direção.

George Santayana uma vez disse que "quem não aprende com a História está destinado a repetí-la." Se levarmos o que eu disse até agora ao pé-da-letra, vamos concluir que a História raramente se repete. Só porque as condições iniciais e o resultado são parecidos com de outro evento, a cadeia de acontecimentos no meio é raramente a mesma. O fato de interpretarmos com a "História se repetindo" é mera coincidência e uma série de enganos que nosso cérebro faz o tempo todo, incluindo Confirmation Bias.

Sei que esse trecho pode ser complexo, mas o ponto que quero chegar é o seguinte: só porque aconteceu uma vez não significa que a mesma cadeia vai acontecer de novo.

As condições iniciais e a cadeia de eventos é o tal "domínio" restrito da "fórmula" que eu disse acima. E como Duncan Watts diria, todo mundo se engana o tempo todo com o conceito de "óbvio". Hoje parece óbvio dizer que é óbvio que o Facebook faz sucesso. Porém em 2002 ninguém jamais poderia prever que em 2012 existiria esse tal Facebook e que seu IPO abriria com a valorização de USD 90 bilhões. Depois do acontecido, podemos tentar traçar os eventos para trás e tentar encontrar as condições iniciais e cadeia de eventos que tornou isso possível, mas nunca antes.

Empiricamente, de tudo que eu já vi oferecido por aí como bala de prata, a "melhor fórmula", parece mesmo muito bom, até que você começa a questionar: "parece uma Fórmula que leva a um bom resultado (Imagem), mas quais as condições iniciais necessárias? Qual o Domínio dessa fórmula?"

E quando você começa a questionar isso, a teoria não se sustenta por 1 segundo. É como Lean Startup, a moda do mundo tech. Veja as condições iniciais do Eric Ries: você vai encontrar exemplos de Groupon, Intuit, HP, empresas que são tudo menos "pequenas startups", porém o livro é evangelizado a todo pequeno empreendedor. Há uma contradição clara de condições iniciais. E o método científicoé muito diferente do que o livro sugere.

"Mas há vários bons exemplos de pequenas startups que seguiram o Lean Startup e se deram bem." E aqui vai outra lição que qualquer um que assistiu CSI sabe distinguir: cena de crime contaminada não serve para nada. Em nenhum dos casos é possível dizer que a "causa" do sucesso foi ter usado Lean Startup, pode haver uma "correlação", e pequena, pois estamos falando de um sistema complexo adaptativo. A borboleta "causou" o maremoto? A probabilidade é muito pequena. Mas daí entra o marketing, como diria Joseph Goebbels, o ministro da propaganda da Alemanha Nazista, "quanto maior a mentira mais acreditarão nela," ou o derivado disso "repita uma mentira vezes suficientes e as pessoas acreditarão que é verdade."

Como Carl Sagan diria "alegações extraordinárias exigem evidências extraordinárias." E isso depende de trabalho exaustivo de testes para quebrar a hipótese. Um medicamente, feito de forma séria, leva anos, até décadas, até chegar às farmácias, mas balas de prata se tornam virais em questão de horas ou dias. E dependendo de quem fala e como fala, ganha status de "verdade inquestionável" muito rápido. Valdomiro da Mundial que o diga.

Porque "parece" que funcionou para 1, 2, 10 casos, não é suficiente para tornar algo uma verdade. A "Teoria" da Evolução das espécies, de Charles Darwin, que tem toneladas de evidências concretas e sólidas, que vem sendo questionada, atacada, dilacerada minuciosamente por mais de 150 anos e que para todos os efeitos e propósitos já é praticamente uma verdade absoluta, ainda não ganhou o status de "Lei", porque qualquer outra coisa menos rigorosamente testado poderia ser diferente?

Conclusão

Na prática, a menos que faça parte do corpo de conhecimento científico já reconhecido e exaustivamente documentado, todo o resto é questionável, deve ser questionado, e nunca deve ganhar status dogmático de "verdade", por definição.

Auto-ajuda não é ciência, é pseudo-ciência, por definição.

Fórmulas que garantem certo "resultado" (Imagem), sem um Domínio, são incompletas e indefinidas, e isso vai necessariamente dividir opiniões (pois cada um tem seu próprio domínio). E só porque um determinado grupo é maior do que quem está questionando, não torna a fórmula verdade. O oposto seria seguir a doutrina de Goebbels.

E o que é de fato ciência comprovada não precisa de quórum, maioria ou "validação". 2 + 2 será sempre 4, não importa quem queira contrariar.

Uma dica: para qualquer assunto que cruzar no caminho, use o Google. Coloque o termo com a palavra "debunked" ou "skeptic" ou "fake" ou "scam" e vai encontrar a resposta. Exemplo, procure por "The Secret Debunked" e veja quanto já foi publicado colocando completamente abaixo a tal "Lei da Atração". Eu pratico isso o tempo todo, nada é inquestionável, não importa quem está dizendo.

Desde minha infância sou um grande defensor de destruir a pseudo-ciência pois ela causa mais mal do que qualquer outra coisa. Lembrem-se, coincidências acontecem, e não são tão raras assim.


[Tradução] Carta para um Jovem Programador Considerando uma Startup

$
0
0

Recentemente compartilhei um artigo criticando Lean Startup - isso está se tornando um hobby. O grande Alexandre Porcelli compartilhou uma palestra de ninguém menos que Alex Payne.

Alex Payne foi um dos engenheiros mais antigos do Twitter, eu o conheço por causa de Scala e seus posts sobre NoSQL e outros assuntos de tecnologia. Mas além de programador, escritor de livros, ele também é um experiente investidor em startups. Sua visão sobre startups neste ponto é similar à minha. Recomendo assistir a palestra, que se originou a partir de um post que ele publicou em seu blog cujo título é o mesmo deste post e cujo conteúdo resolvi traduzir em português porque acho que os Brasileiros precisam ouvir mais outro lado da moeda.

Sem mais delongas, a partir daqui seguem as palavras do Alex:

Eu regularmente recebo e-mails de jovens, normalmente daqueles com interesse em programação, que estão tentando tomar decisões sobre escola e/ou seus futuros profissionais. Este post é para esses jovens.

Se você está no fim da juventude, início dos 20 anos, você cresceu num mundo que idealizou startups, seus fundadores, e as pessoas que vão trabalhar nelas.

Se estiver na escola, talvez você tenha sentido a pressão ou tenha sido incentivado a largar e se juntar ou começar sua própria empresa. Se já estiver fora no mundo de trabalho, talvez sinta que seu trabalho de não-startup é de alguma forma inadequado, ou que você está perdendo experiência valiosa e potencial riqueza.

A geração em que você cresceu tem, nos últimos anos recentes, oscilado à beira de ficar "perdido". Empregos são escassos, entrar numa universidade não oferece nenhuma garantia de emprego. Grandes, velhas corporações não são mais garantia de porto seguro para construir uma carreira. Startups aparentam, através da lente da mídia, como o único sinal de vida num mundo morrendo. Eu entendo o apelo.

O passado de cada um é diferente, e suas escolhas são apenas suas. Isso dito, você não está tomando decisões em um vácuo. Aqui vão algumas coisas para considerar que, em minha experiência, você tem menos chances de ouvir a respeito se estiver trabalhando em startups.

Uma startup é somente um meio para um fim.

Recentemente entrevistei um jovem rapaz. Eu lhe perguntei onde se via daqui quatro anos. "Dirigindo minha própria empresa," ele disse sem hesitar. Eu lhe perguntei porque. "Por que empreendedorismo está no meu sangue," ele respondeu. Não havia nenhuma menção sobre o que sua empresa hipotética faria, que problemas ele resolveria para as pessoas. Seu objetivo era uma empresa apenas por ter uma empresa. Foi para isso que ele estudou, afinal.

As pessoas se satisfazem em seus trabalhos quando operam com um senso de propósito. Então muito do nosso entendimento da psicologia do sucesso gira em torno de objetivos e manter esses objetivos no topo da mente: visualizando o momento da conquista, rastreando o progresso, tendo outros nos cobrando para atingí-los. Objetivos dão forma a nossos futuros individuais.

Talvez uma startups seja o melhor jeito de atingir esse objetivo, e talvez não seja. Se o objetivo do jovem rapaz descrito acima é ter um negócio - qualquer negócio! - então talvez uma startup seja de fato o melhor caminho. Para outros, no entanto, eu sempre me pergunto se eles estão adequando seus objetivos no formato de uma startup porque essa é a aproximação mais aplaudida, admirada, e facilmente entendida (se não facilmente atingida).

Talvez o melhor jeito de atingir seu objetivo seja iniciando uma organização sem fins lucrativos ou entrando na política. Talvez seja fazendo pesquisa numa universidade ou fazendo arte. Talvez a resposta seja se juntar a uma empresa grande, estável com formidáveis recursos para que você possa tirar proveito para atingir seu objetivo. Talvez seu objetivo seja melhor atingido construindo uma carreira estável, sem pressão, que deixe tempo suficiente para sua família, amigos, comunidade e auto-aperfeiçoamento.

Uma startup é somente um meio para um fim. Considere o fim, e procure não festejar com os meios. Com o que você se importa? Quem você quer ajudar? Uma startup facilita ou dificulta atingir esse objetivo? Onde isso vai levá-lo quando seu objetivo for atingido? Onde isso vai te deixar se não atingir?

Um emprego numa startup é o novo emprego de escritório. Cultura de Startup é a nova cultura corporativa.

Startups são retratados como sendo excitantes, arriscadas, e mesmo alternativas subversivas ao tradicional emprego corporativo. Startups são vistas como mais livres, mais abertas, mais flexíveis. Algumas empresas de fato começam assim, mas algumas entrevistas em startups em estágio avançado torna claro quão rápido elas se ossificam em estruturas que se parecem com as organizações que vieram antes delas.

Da mesma forma como foi na primeira bolha da ponto com, atualmente existe uma proliferação de startups, incubadoras, aceleradoras, investidores anjo, e assim por diante. Para a "comunidade startup" conseguir se replicar, no estilo nanobot, as mecânicas de "fazer uma startup" tem sido reduzidas para uma sequência facilmente transportável de ações acompanhadas de um conjunto compartilhado de valores, normas e linguagem. Como consequência, a cultura da maioria das startups são uniformes, até no estilo de vestimenta e enfeites que ajudam os "caras de startup" - e na maioria das vezes são mesmo "os" caras - a ficar com seu rebanho (para não serem apanhados por pumas).

Formados de escolas de negócios como o jovem rapaz que eu entrevistei estão indo diretamente da universidade para startups, e isso se chegam a terminam a escola. Formados em negócios, tradicionalmente avessos a riscos, agora dizem que não querem trabalhar para as grandes empresas. Mas startups são as novas grandes empresas. Eles são, como vou descrever mais abaixo, os campos de escritórios de uma força de trabalho distribuída agregados por venture capitalistas e suas instituições associadas.

Não existe nada inerentemente errado com um emprego de escritório. Apenas entenda no que está se juntando. Quando os barris de cerveja fornecidos pela empresa secam, quando os lanches de graça estão tornando você gordo, e jogar Xbox na sala de relaxamento não é mais tão divertido, e aí? Quando você descobrir que agora se reporta a um gerente médio politiqueiro e não o CEO inspirador que o entrevistou, ainda vai querer estar lá? É um ambiente com brinquedos e novidades o suficiente para sustentá-lo? Quando todos os lugares que você considera trabalhar se parecem mais ou menos os mesmos, até quando vai a novidade?

As Startups foram sistematizadas, mitologizadas, culturalmente e socialmente tiveram seus riscos retirados; reduzidos a fórmulas e receitas. E ainda assim, não existe uma fórmula sustentável para criatividade e rebelião. Quando tentamos industrializar a inovação nós arrancamos exatamente o que estamos tentando cultivar: a destruição criativa que mantém o fogo do capitalismo.

Startups são parte do sistema, não uma chave rebelde nas engrenagens.

O funding para startups - ou seja, o dinheiro que paga seus salários - vem de algum lugar. Indivíduos ricos e instituições investem em startups como qualquer outra classe de ativos. O futurista Bruce Sterling recentemente brincou que "startups estão cheios de jovens trabalhando duro para tornar outras pessoas ricas - financistas Baby Boomers, principalmente". Mesmo isso podendo ser uma afirmação generalista e cínica, de jeito nenhum é uma inverdade.

Em traços largos e excluindo áreas como biotecnologia, venture-backed startups são uma máquina onde relativamente pequenas quantidades de capital são inseridos de um lado e, idealmente, um monte sai do outro lado. (Na realidade essa máquina parece não funcionar mais, embora a efetividade do VC como uma classe de ativos e os próprios VCs esteja aberta a discussão). O ponto saliente: o que está no meio da máquina é você. Você faz ele andar.

A máquina não se interessa por você. De fato, a máquina é desenhada com o entendimento de que a maioria das startups vai falhar, ou no máximo oferecer retornos medíocres aos investidores. A maioria das empresas em muitos portfolios de VCs são reconhecidos como espantalhos. Uma ou duas empresas "10x" é que valorizam o portfolio. No melhor dos casos, fundadores de startups que falham recebem outro empurrão na máquina caça-níquel. No pior dos casos, suas falhas os levam ao desespero.

Não existe nada inerentemente disruptivo numa venture-backed startup. O sistema de startup é somente mais um sistema; uma alternativa à escada corporativa mas com tantos degraus quanto. Algumas startups podem acabar dramaticamente reformulando o mercado, mas assim como qualquer outro operador já no jogo ou regulador ativo.

A agora perene celebração de disrupção dirigida por startups levantam a pergunta: se aceitarmos que disrupção está de fato acontecendo, estamos melhor no mercado disruptivo resultante? Resolvemos um problema, ou apenas mudamos o problema de posição? Criamos valor enquanto aumentamos a justiça e igualdade, criando mudança positiva que vai durar? Ou somente criamos um grupo de pessoas ricas um pouco mais ricas ao custo de outro grupo de pessoas ricas? Estamos criando um futuro melhor ou somente embaralhando o presente?

Startups tem um custo interpessoal em andamento.

Nenhuma história de uma startup está completa sem uma cena ou duas de heróis machos: noites gastas programando até a exaustão, semanas longe de casa tentando levantar dinheiro em pitch humilhante atrás de pitch humilhante. Startups apelam a um desejo de ousadia que está perdida em muitas formas de trabalho moderno, e ouvimos histórias das recompensas esperando quando vidas pessoais são sacrificadas no altar do cronograma de lançamento.

Menos frequentemente ouvimos sobre os danos que startups fazem à vida das pessoas. Em casos extremos, como com o empreendedor cuja história de fracasso e suicídio esta ligado acima, a "comunidade" pode tirar um dia ou dois para refletir e lamentar em blogs e tweets. Então estão de volta à "moeção", e mais importante, fazer um show público de quão duro você está trabalhando para satisfazer os investidores e a intimidadora concorrência.

Eu vi em primeira mão os danos que startups podem fazer a relacionamentos. Eu vi casamentos e amizades destruídas, vi crianças e parceiros sendo colocados de lado, e falhei com aqueles na minha vida de todas as maneiras quando o trabalho veio à frente. Eu ouvia enquanto as pessoas que são o retrato de uma startup de sucesso - visíveis na imprensa e mídia social, liderando conferências, eternamente fundando e saindo - confidenciando sua total solidão a despeito de aparentemente ser o centro social da comunidade empreendedora.

Quando você é jovem, relacionamentos parecem um recurso renovável. Amizades existem aos montes e vem de forma fácil. Acabando de sair de casa e para o mundo, você está ansioso para escapara da presença constante da família. Ser puxado para o trabalho o faz se sentir importante, independente, um adulto. O trabalho por si fornece uma nova comunidade e novas amizades, e as ligações formadas durante intenso trabalho colaborativo são fortes.

Eu fiz muitos amigos através do trabalho em empresas em fase inicial. Ironicamente, esses relacionamentos são mais fáceis de manter e aproveitar quando não estou trabalhando numa startup.

Pensamentos de Partida.

Eu trabalhei em startups por metade da minha vida já. Esse trabalho me trouxe liberdade financeira, e eu coloquei dinheiro que ganhei numa startup em outras startups. Se existe, como eu argumentei acima, um moderno "sistema de startups", eu sou parte disso.

Não vou me equivocar: eu sou profundamente cético com esse sistema. Sou cético quanto à escravidão desse sistema, fetichização autocongratulatória de "disrupção" e ao mesmo tempo se tornando tão obviamente o tipo de instituição impassível que quer substituir. Sou cético quanto à visão de curto-prazo da comunidade de startup. E sou particularmente cético pelo seu desprezo quanto às vidas das pessoas que participam e a vida daqueles que vivem no mundo que as startups procuram reformular. Não vamos nem começar a discutir o conluio, cartelização e outras atividades de corrupção de mercado que são tão comuns no mundo dos VCs. O ponto sendo: é um jogo ruim, e um jogo de fraude.

E mesmo assim. Existem startups que eu não gostaria de ver desaparecer. Existem pessoas trabalhando e fundando essas startups que são boas, gentis, com vidas pessoais e profissionais equilibradas, preocupadas com o impacto dos seus trabalhos. Da mesma forma como jogamos calúnias e acusações de corrupção em outros sistemas como política, mídia de massa, entretenimento e esportes profissionais, também devemos admirar aqueles que operam de forma ética e eficiente neles. Devemos ainda mais celebrar aqueles que são pioneiros em sistemas novos e alternativos, pois eles trabalham nas sombras da comunidade que tem uma mão constante na manivela da máquina publicitária.

Agora, você poderia dizer que estou jogando muita responsabilidade aos pés do mundo de startups. Embora esse sistema espalhe todos os dias sobre ele mesmo como o salvador de tudo, do capitalisto à cultura, claro que podemos aceitar que negócios são negócios e ideais são melhores deixados em casa. Como um VC de uma empresa de topo de Sand Hill Road me disse durante um pitch muitos anos atrás quando descrevia uma funcionalidade conceitual no Simple que facilitaria aos usuários doar mais facilmente e regularmente uma porção das suas economias para a caridade, "não vamos desperdiçar tempo nessa coisa, estamos aqui para fazer dinheiro."

Você poderia aguentar essa conduta, mas eu espero que seu idealismo não tenha sido desgastado quando ainda tão jovem. Eu espero que você queira que seu trabalho tenha significado, propósito, e valor não importa que forma esse trabalho tenha. Mais do que isso, eu espero que você queira que sua vida seja definida por mais do que seu trabalho.

Jovem programador, eu insisto que você considere ambos os lados da moeda da startup. Existem tantas formas de deixar uma marca no mundo.

Uma Nota quando a Feedback

Se você é parte do público algo deste post, adoraria ouvir seu feedback e responder suas perguntas, preferencialmente por e-mail. Meu endereço é fácil de encontrar neste site.

Se você nãoé parte do público algo e está muito muito bravo que um estranho na Internet tem uma opinião diferente da sua, eu o encorajo a direcionar sua energia em um argumento alternativo que alguém jovem (ou, realmente, qualquer pessoa num ponto de transição de carreira) poderia se beneficiar lendo. Eles são seus futuros funcionários, colegas de trabalho ou fundadores. São eles que você precisa convencer, não eu.

— 23 de Maio de 2013

[Off-Topic] Agile feito Errado

$
0
0

Uma das coisas mais comuns que já vi em inúmeras empresas é tentar adotar alguma metodologia com a esperança que isso melhore as coisas. Invariavelmente não muda nada e até piora. Já repeti isso inúmeras vezes e vou repetir de novo: Agile não é uma bala de prata, não é uma receita mágica que vai consertar maus profissionais. Maus profissionais não tem conserto. Para ficar claro: um profissional júnior, que ainda tem deficiências técnicas e tem vontade de crescer, vai conseguir aprender técnicas, práticas, ferramentas e aumentar em qualidade técnica, em produtividade, em eficiência. Um mau profissional, que pode até ter alguma capacidade técnica, mas que está mais interessado em pouco trabalho, pouco esforço, pouca responsabilidade, e chega a ser mal caráter na sua conduta e atitude, não vai mudar.

Agile é mais uma fórmula cuja imagem (resultado) é a promessa de hiper-produtividade que pode chegar a 500% do atual. Novamente, qual o Domínio (premissas)?

A premissa mais básica para Agile é que a equipe tenha capacidade técnica e comprometimento. Isso nenhuma metodologia vai ensinar ou mudar, ou cada indivíduo da equipe é uma pessoa capaz ou simplesmente não vai funcionar. Fazer código bem feito, bem organizado, seguindo as boas práticas conhecidas, com testes, com disciplina, etc é o mínimo necessário para começar, é o arroz com feijão. Se isso já não existir, desista, nenhuma metodologia, nem macumba, nem exorcismo, nem feng shui, vão ajudar. O que vai acontecer ao se colocar Scrum ou práticas de XP ou Kanbam ou qualquer outra coisa numa equipe ruim, com liderança fraca, são algumas das seguintes prostituições das boas práticas:

Planning para Inglês ver

Um bom planning só é possível se o Product Owner se dedicar a ter um backlog priorizado e bem definido. E entenda, a definição das User Stories deve estar pronta antes do Sprint Planning. Uma User Story vaga, mal definida, não vai ajudar em nada. Enquanto a equipe está trabalhando no Sprint, o PO deve definir as User Stories, pode se juntar a um ou mais membros da própria equipe ou de equipes paralelas para ajudar nisso. Durante o Sprint Planning, tudo já deve estar bem definido, já pré-pontuado de preferência, priorizado e a equipe vai meramente pontuar o Sprint e, de acordo com a Velocidade atual, ver o que cabe no próximo Sprint. É uma reunião que, se bem feita, vai durar 1 ou 2 horas no máximo.

Sintomas: se demorar mais que 2 horas, tiver pessoas demais só para fazer quórum, User Stories mal definidas que geram discussões intermináveis e nenhuma ação, você está fazendo errado.

Pair Programming para Inglês ver

Pair Programming é uma prática muito boa, a equipe decide como praticá-la. Porém, você pode encontrar um membro da equipe que quase nunca programa bem sozinho e ao fazer o "pair", é mais ouvinte do que um participante - o famoso "peso morto". Só fica dando pitaco ou mesmo só fica em silêncio (jogando Candy Crush ou navegando no Facebook). Ou seja, não tem valor algum.

Sintomas: Isso é mais fácil de acontecer em equipes muito grandes (acima de 6), pois fica mais fácil se "esconder" fazendo pair-fake com diferentes pessoas. Aliás, eu nunca recomendo mais do que 4 pessoas numa equipe ágil. Mesmo sendo um único produto, se tiver 15 pessoas, por exemplo, melhor dividir em 3 equipes de 5 pessoas. Melhor ainda se as pessoas rotacionarem nessas equipes para que não se crie "panelas" onde um ruim protege o outro ruim.

Protecionismo Departamental

Programadores profissionais hoje precisam ser multi-funcionais, polivalentes. Obviamente cada um tem um tipo de capacidade melhor do que a outra. Por exemplo, o que chamaríamos de um "front-end", deve ser o mestre do HTML 5, CSS 3, Javascript, mas também deve saber o mínimo de Rails, Node.js, Python ou o que for o back-end para que consiga organizar os templates, já integrar com controllers, models, helpers, etc. E os "back-ends" também devem minimamente saber HTML, e mesmo infraestrutura.

Pontuação de User Stories (seja em Story Points, Working Hours, etc) deve ser sempre o mesmo não importa quem puxe a User Story. Não existe isso de: "essas 3 stories são minhas, essas 2 são do Fulano, essas 4 são do Ciclano". As stories são priorizadas e cada um vai puxando uma story de cada vez à medida que termina a anterior.

Sintomas: stories que são pontuadas diferentes dependendo da pessoa que vai pegar, ou stories "reservadas", ou tipos de stories que só um "tipo" de profissional pode pegar. Não existe isso de "esta story é de infra, só um cara de infra deve poder pegar, e o cara de back não deveria estar olhando pra ela". Sprints são responsabilidade da equipe toda.

Uma das vantagens de pair programming e equipes pequenas é cada um aprender novas capacidades com os outros. Um back pode puxar uma Story que tem alguma coisa de front, mas ele não é o melhor de front, mas na hora que precisar nada impede de pedir pair com um bom front da equipe para ajudar. Uma User Story pode demorar 2 horas se for feita por um bom back, mas pode levar 5 horas se feita por um front.

O correto é que o cara de front se aproxime das 2 horas e não que a story seja reservada. O correto é a velocidade da equipe aumentar gradativamente, e para isso a velocidade individual de cada membro deve aumentar gradativamente. E para isso ele deve constantemente aprender novas capacidades. É assim que todos evoluem. Reservar stories é o melhor exemplo de batedor-de-ponto querendo 'proteger' seu emprego e esconder sua mediocridade.

Qualidade para Inglês ver

Todos os membros da equipe devem estar preocupadas tanto com a qualidade técnica do código quanto com a qualidade funcional (se a funcionalidade realmente se comporta da forma correta com os usuários finais). Muitas vezes existem equipes que alocam pessoas com o objetivo único de ser um "Q&A" (Quality Assurancy), que se responsabiliza de montar casos de testes, automatizar testes de integração, alinhar com o PO e com analistas de negócios, marketing, etc.

Vou dizer que pessoalmente não gosto dessa distinção, mas confesso que em alguns casos faz mesmo muito sentido. Só que isso deve ser feito de forma que não seja retirada da equipe a responsabilidade de entregar funcionalidades que de fato funcionam, não pode existir a sensação de "foda-se se tiver bugs, depois o Q&A pega". Mas é isso que acontece muitas vezes.

Sintoma: procure User Stories que são entregues, depois rejeitadas, depois refeitas, depois rejeitadas e ficam assim por muito tempo. Uma User Story que fica 1 mês sem ser aceita é um absurdo monstruoso, uma abominação. Pior ainda se existir membros da equipe que, quando você olha o backlog passado, vai ver que está sempre envolvido em stories que sempre são rejeitadas e sempre demoram dias pra serem aceitas. É o típico caso do profissional que mantém seu emprego resolvendo problemas que ele mesmo criou. Remova essas maçãs podres da equipe o quanto antes, ela contamina o pote todo muito rapidamente.

Desculpas, Desculpas, Desculpas

"Não deu pra fazer porque faltou teste, mas o cara que faz teste estava sobrecarregado então travei."

"Não deu pra fazer porque tinha um obstáculo na outra equipe que não resolveram, então travei."

"Não deu pra fazer porque estava mal especificado, mandei um e-mail pra tirar a dúvida, ninguém respondeu, então travei."

Um bom profissional dá soluções, não aponta culpados ou fica procurando desculpas para justificar a própria incompetência. Significa que ninguém pode reclamar? Claro que não, sempre existem problemas difíceis de resolver e as pessoas podem e devem sempre procurar ajuda quando necessário.

Sintomas: Mas é fácil identificar o procrastinador profissional. O sujeito nunca está na mesa dele, passa mais tempo conversando no café do que trabalhando. Chega tarde todo dia, sai cedo todo dia. Quando está na mesa você olha e o infeliz está no YouTube! ou no Hacker News. E que fique bem claro: nenhuma dessas atividades, isoladamente, é um problema. Até eu dou uma pausa e fico no Facebook. Só que quem tem esse comportamento constantemente, rotineiramente, está de palhaçada. E pior: se no final do dia a tarefa dele está terminada, com código bem feito, sem bugs, etc, eu não reclamaria, mas normalmente não está: a tarefa está incompleta, código mal feito, que sempre precisa se refeito. E o sujeito vem com desculpas parecidas com as que mostrei acima.

Isso é ser mal caráter, é atitude de criminoso. Ele está deliberadamente roubando a empresa, seu salário está sendo pago e o valor não está sendo entregue. É um ladrão.

Um bom profissional, que trabalha sério, não espera ser cobrado para dizer "ah, eu perguntei, ninguém respondeu, então não fiz". Um bom profissional, quando encontra um obstáculo, corre atrás do problema. Se for uma dúvida técnica, pergunta aos colegas do lado (pessaolamente, e-mail, chat, gtalk, etc). Se o problema for funcional, corre diretamente ao PO. Se o problema for departamental, ele mesmo levanta da cadeira e vai até a outra equipe para tirar a dúvida. Se a tarefa depende de um terceiro que não quis colaborar, ele vai diretamente ao seu chefe para resolver o problema. Enfim: o sintoma básico do mal profissional é que ele não vai atrás, ele fica contente quando encontra o obstáculo porque daqui uma semana, só na próxima reunião de Review, ele vai dizer "puuuuuts, eu até tentei fazer, mas o Fulano não colaborou, então não deu". Tá de brincadeira.

Conclusão

Se sua equipe tiver apenas 1 dos problemas descritos acima, já é uma catástrofe. Se tiver mais de um, é um problema de intervenção militar, nível Iraque. Mais do que isso, só uma bomba nuclear para resolver isso. E pior ainda, alguém que apresenta todos os maus comportamentos descritos acima é um sociopata criminoso. Não adianta dar bronca, não adianta tentar consertar: não tem conserto ... mas tem solução: trocar as maçãs podres o quanto antes.

E novamente, para ficar claro, é óbvio que existem todos os tipos de maus profissionais, não estou falando somente de programadores. Existem maus gerentes, maus analistas, maus coordenadores, maus supervisores, maus diretores, etc. Isso vale para todo mundo.

Se você é um programador e está vendo tudo isso, corra atrás, mas se o problema for na diretoria sinto dizer que não há o que fazer. Agora, se você é um gerente, um supervisor, alguém de cargo de autoridade, e não está tomando nenhuma atitude sabendo de tudo isso: se demita, você está fazendo muito errado e sendo conivente com um ambiente cuja cultura é o mau-caratismo.

Aplicar 'algumas' práticas Ágeis, interpretadas da maneira errada, não é ser Ágil, é se enganar. É como numa dieta nutricional. Durante o dia segue a dieta à risca, mas quando ninguém está olhando come chocolate e doces e no dia seguinte fala "puts, não sei porque essa dieta não funciona, estou seguindo direitinho, acho que a dieta não funciona, vamos tentar outra." #lamentavel

[Off-Topic] Programadores são péssimos Comunicadores (UDP vs TCP)

$
0
0

Nós, programadores, vivemos na Matrix: achamos que sabemos o que estamos fazendo e que o resto do mundo é idiota demais para nos entender. Afinal, todos sabemos ir num Github, ler código, discutir no Hacker News e no Reddit e o resto do mundo só sabe postar no Instragram e no Facebook. Portanto, obviamente somos melhores e quem não nos entende é que deve se esforçar para mudar.

Tendo vivido em todos os lados por muito tempo tenho uma novidade para vocês: programadores são péssimos comunicadores. Existe uma impedância de comunicação que está se tornando seriamente uma incapacidade. A impressão que temos nas comunidades de programação é que o código é a única linguagem universal. Só que "show me the code" não é tudo. Algum tempo atrás eu respondi no Quora que a coisa mais difícil para todo Engenheiro de Software entender é que 90% dos problemas de um projeto não se resolvem com código.

Sem querer generalizar, mas só para ilustrar: no mundo open source (para quem participa) dá a impressão que é o contrário, mas note que a grande maioria faz pequenas contribuições, esporadicamente, e mesmo quem participa mais ativamente, ainda assim é uma experiência fragmentada. De fato o software estará pronto só quando estiver pronto. E com isso a grande maioria dos projetos open source, na verdade, fracassa.

Para cada um JQuery que dá certo, dezenas de outras bibliotecas Javascript nem são reconhecidos - mesmo tendo alguns aspectos tecnicamente melhores. Os grandes e melhor coordenados, e que tem força bruta (colaboradores de sobra), costuma andar melhor. Mas não é eficiente, apenas aparenta ser. E os que são grandes demonstram uma organização bem diferenciada, com datas de lançamento, roadmap de features e começa a convergir para o que conhecemos como "projetos" de verdade.

No mundo real, não temos nem tanto tempo sobrando, nem tanta gente sobrando, os riscos diretos são muito maiores, e queremos ter mais controle sobre os resultados. Não vou nem entrar no mérito das melhores maneiras, mas fundamentalmente, no mundo real comunicação é a diferença entre fracasso e sucesso.

Informar não é Comunicar!

A primeira coisa que você precisa entender é o seguinte: só porque a informação "existe" ou você colocou em algum lugar compartilhado, isso não é comunicar.

Comunicação tem 4 pontas: o comunicador, o recipiente, a mensagem e o meio de transmissão. Os programadores normalmente assumem 2: o comunicador (ele mesmo) e a mensagem, o resto é ignorado. Vamos definir isso melhor:

"Comunicação só acontece quando o recipiente recebe e entende a mensagem. Se isso não aconteceu, não existiu comunicação."

Vamos falar descer um nível e falar em "geek": existe um cliente, um servidor, um protocolo e um meio de transmissão. Se você empacotou a mensagem de acordo com o protocolo, abriu a conexão com o servidor, tentou enviar a mensagem, mas ele não terminou e voltou com erro, nós sabemos que a comunicação não existiu.

No mundo de TCP/IP, primeiro mandamos um SYN (que inicia a comunicação), recebemos de volta um SYN-ACK (acknowledgement do servidor dizendo que recebeu o SYN) e enviamos um ACK para indicar conexão estabelecida. Realizamos esse handshake e conseguimos sequenciar o envio e recebimento de pacotes de forma que garantimos que o que foi enviado foi inteiramente recebido.

Nessa metáfora, eu diria que a maioria dos programadores entende melhor UDP, eles enviam datagramas de informação e não se preocupam se o recipiente recebeu todos os pacotes, simplesmente vai enviando. Dá mesmo a impressão que programadores pensam UDP, olhem só:

  • não querem esperar um handshake pra garantir que a conexão foi estabelecida
  • mandam pacotes pequenos de informações fragmentadas, pouco overhead de protocolo
  • TCP se preocupa com congestion control e faz throttling, UDP vai mandando mesmo se o roteador dropar os pacotes
  • se um pacote se perde, UDP não se preocupa em reenviar
  • pensa que parece mais eficiente fazer broadcast e multicast

Funciona bem para comunicação para grandes grupos, broadcast, onde se uma porcentagem receber a mensagem já é suficiente. Eu diria que UDP até que funciona bem no mundo fragmentado de open source, mas num mundo onde estabelecer uma conexão e garantir a entrega da mensagem é importante, vamos de TCP.

TCP funciona porque mesmo com uma conexão ruim, mesmo com um servidor meia boca, você controla o stream de dados e garante que todos os dados foram recebidos, na sequência correta, e o 100% do que foi enviado foi recebido. Em UDP, se o meio de transmissão é ruim, se o servidor recebe a informação corrompida, ele não se importa, simplesmente vai enviando.

Ambos os protocolos tem utilidade, porém se precisamos ter certeza que a informação foi recebida corretamente e integralmente, precisamos usar TCP. Eu diria que no mundo open source, não tem problema você usar UDP pra se comunicar, diminuir a latência e ser mais "eficiente". Mas no mundo não-open source (e isso significa não só software, mas fora de software), precisamos ser mais TCP. As vantagens do TCP?

  • garantir que a conexão foi estabelecida antes de mandar informações
  • garantir a ordem da informação, rearranjando pacotes se necessário
  • moderar a velocidade da transmissão pra não floodar o outro lado
  • garantia da entrega da mensagem, não só da transmissão
  • checagem de erro, pra garantir que a informação não foi corrompida
  • "acknowledment", garantir que o outro lado recebeu e entendeu a informação

Vêem a diferença? Parece que demora mais, mas é aquela velha história: entregar rápido mas ter que ficar entregando diversas vezes acaba ficando mais lento do que garantindo que na primeira vez foi entendido. É que nem fazer código sem testes, parece que é mais rápido, mas depois vem as consequências.

Portanto, programadores, ajustem seus protocolos, sejam mais TCP do que UDP.

[Off-Topic] Tech Startups, superlotação de B2C. Boring.

$
0
0

Quando estive em São Francisco em 2012, participei da Techcrunch Disrupt (pessoalmente, boring). De qualquer forma, dentre as poucas coisas que me lembro, foi uma coisa que Reid Hoffman, fundador do LinkedIn, mencionou. Não lembro a frase exata mas em resumo foi algo como:

"Todos estão fazendo startups B2C, quase ninguém está resolvendo o problema 'Enterprise', o mercado B2B de bilhões de dólares."

Me deixe simplificar, estou considerando "B2C", business to consumer, como todos os sites comuns que vemos hoje: redes sociais, instant messaging, content management, enfim, tudo cujo público alvo final é a massa de consumidores pessoas-físicas. E estou considerando "B2B", business to business, como todas as soluções baseadas em software que resolvem problemas tanto internos de uma corporação quanto problemas que interligam múltiplas corporações, ou seja, soluções onde o público alvo final são pessoas jurídicas. É uma simplificação, mas ajuda meu propósito.

Vou repetir uma coisa que twitei recentemente: "empreendedor" não é uma carreira e não é sequer uma profissão. É um estado. A irrelevância é que qualquer pessoa, física ou jurídica, por definição, é um empreendedor. Até mesmo um estudante está empreendendo, empreendendo a construção do seu conhecimento para resolver algum problema no futuro. Uma diarista é uma empreendedora, e hoje algumas ganham até em escala subcontratando outras e distribuindo serviços. Não são apenas desenvolvedores de software que, por acaso, ganharam esse "privilégio". Na prática, não há nada de especial em ser empreendedor, basta não ser um procrastinador.

Muitos jovens, muitos inclusive ainda nem na faculdade, estão iludidos pelo falso "glamour", deste curto período que pode até ser considerado uma bolha, ou pelo menos uma anomalia temporária de mercado. Se ainda você está neste grupo, recomendo que leia meu artigo anterior, a tradução do post de Alex Payne, Carta para um Jovem Programador Considerando uma Startup.

O exercício aqui é simples, porque vocês acham que a maioria esmagadora de tech startups está investindo em aplicações Web B2C? Em particular, porque todas elas são muito parecidas entre si? Basicamente se você mudar o logotipo, o nome e algumas cores, muitas delas são praticamente idênticas. Ecommerces são óbvios. Redes sociais verticais. Misture um componente de rede de conhecidos a verticais como empregos, classificados, transporte (táxi), exercícios e saúde, etc.

A resposta é assustadora em sua simplicidade: porque isso é tudo que uma pessoa inexperiente conhece. Seu dia a dia se resume a usar Facebook, Instagram, Medium e Tumblr. Se resume a pegar ônibus, metrô. A ir num pronto socorro ou fazer alguns exames. A ir na academia. A frequentar uma universidade. E tudo isso, do ponto de vista de um consumidor, nunca do provedor ou produtor. Portanto, tudo será mais ou menos igual.

O Mundo Corporativo é Muito Chato

O efeito colateral mais assustador ainda, é que as "tech startups" e a pseudo-carreira de "empreendedor", chegam como a salvação daqueles que leram blogs e tweets e posts em Facebook falando como é 'horrenda' a vida de um trabalhador numa agência, numa grande empresa, numa corporação. A burocracia, a politicagem, a estrutura hierárquica. Tudo isso foi pintado como que se arranjar um emprego fosse equivalente a andar pelo corredor da morte e como se estar num emprego numa corporação fosse estar sendo diariamente torturado de maneira cruéis.

E o pior: a maioria dos jovens e inexperientes acreditam nisso. TL;DR: não é inteiramente verdade.

Para piorar a situação que já é ruim, de fato aparecem Cisnes Negros pelo caminho. Facebook, Tumblr, Snapchat, Twitter. E isso dá a impressão que é possível repetir. Não entender o que é a Lei dos Grandes Números pode ser fatal. Em resumo: dado milhões de pessoas tentando ganhar na loteria, eventualmente um ganha. Mas não há nenhum processo repetível que leva a isso. É um beco sem saída da aleatoriedade. Agradecimentos a Bernoulli e Poison por nos avisar disso, embora poucos saibam disso hoje.

Não quero dizer que não exista valor em B2C, claro que há, porém muito mais limitado do que se imagina. O outro lado da moeda, o mundo corporativo, é quase imune às startups. Conhecemos os grandes nomes, SAP, Oracle, Microsoft, Totus. Sabemos que do lado 'puramente técnico', são softwares de baixíssima qualidade que - 'magicamente' para alguns - movimenta bilhões de dólares. E é um mercado que vê pouca ou quase nenhuma disrupção em décadas.

E o motivo disso é simples: poucos pararam para entender como esse mundo funciona. Por exemplo, quem está tentando entrar no mundo de ecommerces. O entendimento simples é que um ecommerce é uma "loja virtual". Eu vejo diferente, eu entendo ecommerce como mais um canal de distribuição. Por trás existem inúmeros processos consolidados, que qualquer um que tenha trabalhado em varejo ("retail") conhece muito bem. Centros de distribuição, Supply Chain, estratégias de precificação, tributação e transporte de materiais, aprovação de compras, inventório e warehouse management, etc. Como disse, território para qualquer um que tenha trabalhado com Sales and Distribution. Alienígena para qualquer outro.

Sites de empregos. Poucos entendem o que isso significa mas é uma pequena perna na área conhecida como HR, Recursos Humanos. Significa entender CLT, entender o que significa liability trabalhista, benefícios, recrutamento e seleção, treinamento, folha de pagamento, timesheet, viagens, etc.

Entenda: você não tem a mínima condição de oferecer qualquer coisa ao mercado corporativo sem ter participado ativamente do mundo corporativo. Para quem nunca esteve nesse mundo, uma corporação é uma caixa preta. E o problema é que a maioria das pessoas imediatistas costuma se afastar daquilo que não entende, em vez de tentar explorá-la. Um "hacker" faz exatamente isso: quebra caixas pretas. Quem apenas consome não é um hacker, é apenas um script kid achando que é mais do que é.

Quando se passa por diversas empresas, diversos contextos e situações, diferentes processos em diferentes etapas, só então começa-se a entender o domínio do problema. Faça isso algumas vezes e possivelmente você pode começar a enxergar como melhorar ou, melhor ainda, "inovar" nesse segmento. Inovação no mercado corporativo se inicia com otimização simples de processos, e pode terminar com a substituição completa de um processo antigo por um novo ou a criação de uma vertical inteiramente nova que não existia antes. Mas inovação, por si mesma, é um processo, com etapas que precisam ser ultrapassadas uma por uma. E a primeira etapa é invariavelmente 'adquirir conhecimento'.

Não há atalhos. Mesmo no mundo B2C me admira alguém tentar criar um produto para massas de consumidores sem entender as técnicas de convencimento de massa acumuladas sobre o corpo de conhecimentos conhecido como "marketing".

O atalho dos jovens de se criar tech startups copy-cats acreditando cegamente numa promessa inexistente está criando uma geração que desconhece todos os processos que regem este enorme mercado. E algumas coisas são dependentes de tempo. Eu me vali dessa vantagem ao entrar cedo no mercado com o objetivo de descobrir como funcionam suas engrenagens. Quando se é o mais novo, erros são considerados menos ruins, acertos são supervalorizados. Quando se passou da idade, erros são intoleráveis e acertos são considerados o normal. E quanto mais tarde for, menos chance você terá de entender como o mundo funciona, mais e mais portas estarão fechadas.

Obviamente estou generalizando, existem casos - excepcionais - onde isso funciona diferente, mas se você não sabe se é um caso excepcional ainda, as chances são que você não é.

Serendipidade só acontece quando você se expõe à maior quantidade de situações diferentes possíveis, criando oportunidades. Em um único "produto", uma única startup, uma única idéia não provada, as chances de serendípede são reduzidas. A vantagem de uma SAP nas suas várias décadas de existência, foi acumular o conhecimento de todos os processos, de todas as indústrias, varrendo praticamente tudo que existe de importante do mundo corporativo. E mesmo assim há áreas que ela não consegue tocar direito ainda, o que possibilita uma Salesforce.com de se sobressair, por exemplo. Imagine quem está artificialmente se limitando.

Mais do que isso, ao não se ter conhecimento nenhum e com a ilusão que "erros" são bons porque viram aprendizado, estamos esquecendo o óbvio: errar é humano, errar o que já é notório que é errado é burrice. Pura e simplesmente assim. Cobrar um cliente por um serviço sem ter assinado um contrato antes e depois chamar o cliente de desonesto por não pagar tudo - apesar de errado - demonstra estupidez de quem não assinou o contrato. Gastar o dinheiro antes do tempo como "investimento" e depois ter problemas com folha de pagamento, novamente, é amadorismo por desconhecer o básico de fluxo de caixa. Reescrever sistemas inteiros sem saber quanto vai custar não só as horas de desenvolvimento, mas o treinamento de todos os usuários, as integrações com outros sistemas, a migração de dados, o custo de oportunidade, etc, é outra enorme burrice de quem não sabe fazer análise básica de custos. Tudo isso é simples burrice, não precisa errar pra aprender. Bastava ter visto isso acontecendo uma vez para saber que existe, toda empresa faz isso, há décadas, muito antes de você nascer.

Isso é inexperiência, que é algo normal, ninguém nasce sabendo tudo. Por isso adquirir experiência é tão importante, ou você estará sempre em desvantagem porque quis um atalho que acharia que economizaria 5 anos e vai fazê-lo ficar 10 anos atrasado.

O mundo B2C está super-saturado. O mundo B2B vai permanecer isento de startups importantes por mais alguns anos já que esta geração também está desperdiçando tempo e esforço de maneiras ineficientes, desperdiçando tudo que já sabemos há anos. E lembre-se: evitar desperdícios - que eu me lembre - é o pilar do "Lean".

[Coletânea Facebook] Pensamentos Aleatórios sobre Startups, Capitalismo, Carreira

$
0
0

Nos últimos meses o local onde tenho mais exposto opiniões não é nem aqui no blog (que reservo para artigos mais elaborados) nem mesmo no meu Twitter (que tenho reservado para conversas rápidas ou apenas links interessantes), mas sim no meu perfil no Facebook - basicamente por conveniência. As opiniões que publico, na realidade, seguem uma linha de raciocínio. Quero testar colocá-los em ordem aqui no blog de tempos em tempos para que seja possível ler tudo de uma vez. Me digam o que acham nos comentários.

A razão de alguns posts serem em português e outros em inglês é basicamente se o conteúdo faz mais sentido só para Brasileiros ou se ele é mais Universal.

Finalmente, durante meus posts eu também publiquei alguns artigos aqui no blog que complementam o assunto:

Sobre Startups

Sobre Empreendedorismo e Capitalismo

Sobre Carreira

[Off-Topic] Trabalho Remoto - Small Office, Home Office (SoHo)

$
0
0

Update: Esse é assunto é mais complicado e alguns pontos talvez precisem de mais explicação. Depois vejo se faço um adendo ou um post para complementar, mas até lá, se alguém discordou talvez seja na linha que o @porcelli também discordou e tentei explicar via Twitter, embora seja mais para dizer que estamos concordando em concordar do que realmente uma discordância :-) Acho que o principal ponto é: não sou contra trabalho remoto, pelo contrário. O TL;DR é que não acredito que seja a "única" solução e a intenção é explorar alguns cenários, apenas isso. Conheço excelente profissionais que trabalham remoto. Eu mesmo, como digo no artigo, tenho equipes geograficamente separadas, portanto não é este o ponto discutido.

Para variar SoHoé um dos diversos assuntos que a maioria de nós pensa apenas superficialmente e decidimos "é bom" ou "é ruim". É ruim que Marissa Mayer proibiu Home Office. É bom que a 37signals está evangelizando Home Office. É ruim que a Microsoft concorda com Home Office mas com uma possível visão distópica. Lembrando que SoHo é um assunto que a indústria discute praticamente desde o advento do computador pessoal no meio dos anos 80, isso não é de maneira nenhuma algo recente.

Um disclaimer, eu sou dono de um software shop, uma empresa que oferece serviços de desenvolvimento de software e, portanto, o core da empresa são programadores contratados não somente em São Paulo (onde moro e tenho clientes), como Porto Alegre, Fortaleza e Natal. Não pretendo entrar no meu processo neste artigo.

Eu pensei em dissecar o livro Remote, que é a referência atualmente. Mas não há muito o que dissecar. Se você ler o índice, basicamente já sabe tudo o que o livro diz, o resto é fermento. Veja só:

  • A Hora é Essa para Trabalho Remoto - vai contar tudo que já conhecemos como pontos negativos de grandes escritórios: as reuniões longas, as interrupções, a distância para se locomover, a qualidade de vida. Acho que sem ler o capítulo já sabemos exatamente o que nos incomoda. O problema é que esse é um ponto difícil de contra-argumentar, a premissa é que qualquer lugar vai ter coisas que agradam e coisas que desagradam. Listar o que desagrada não é necessariamente o mais relevante. Por exemplo, se você tem namorada ou é casado, você fez uma lista de itens do seu parceiro(a) comparado à Miss Universo ou ao Mister Universo? É o conjunto de coisas que desagrada que importa ou as coisas que agradam?

  • Lidando com Desculpas - novamente, seriam os "argumentos", "demagogia", "retórica" que, no caso, assume-se que é o que os gerentes e donos de empresa usam de desculpas para não permitir trabalho remoto. E o problema é que metade não são o que as empresas dizem mas o que os vendedores de auto-ajuda motivacional dizem. "Trabalhar próximo aumenta troca de informações e portanto a criatividade". Outras são as políticas como "segurança dos dados", atendimento ao cliente, perda do controle. E de fato, esses são itens secundários. Como dono de empresa, a maioria ou todos os pontos são fáceis de lidar e concordar. Acreditem, a indústria não é tão ruim assim, e nenhum desses pontos passou em branco nas últimas décadas. Ainda há resquícios, sem dúvida, mas compare hoje com 20 anos atrás e verá a diferença.

  • Como Colaborar Remotamente - resumindo o que já sabemos: a tecnologia diminuiu as distâncias. Skype/Hangout/etc, Basecamp/Tracker/Asana/etc, Github/Bitbucket/etc, IRC/Campfire/etc. Fora que muitos outros serviços terceirizados já são remotos, contador é um bom exemplo - e vou retornar neste ponto, então lembrem dele.

  • Cuidado com os Dragões - esta é a única parte que "balanceia" o livro e dizer que trabalho remoto não é sempre flores. Não se sentir sozinho, não trabalhar mais horas do que deveria, ter hábitos saudáveis, etc. São dicas que valem não somente para trabalho remoto mas hábitos normais de qualquer um. Resumindo: tenha bom senso, mas você já sabia disso.

  • Contratando e Mantendo os Melhores - resumindo, e eu concordo, existem profissionais excelentes em todos os locais do mundo - meu motivo para explorar fora de São Paulo. Novamente, é muito simples evangelizar um argumento e não levar todos os pontos possíveis em consideração. Como disse, concordo com o ponto e vou retornar a ele depois.

  • Gerenciando Trabalhadores Remotos - mais uma vez, bom senso de gerenciamento. Bons gerentes já sabem disso. Acreditem, temos multinacionais funcionando em todos os continentes faz algumas décadas. Somos bons nisso. A premissa que muitos acreditam, de que as empresas não querem trabalho remoto é porque não sabemos gerenciar trabalhadores remotos. Isso não é inteiramente verdade, veja o massivo movimento de outsourcing de serviços pra países como Índia. Como nos jornais, só nos focamos nas más notícias, mas acreditem, sabemos fazer isso. E só tem ficado mais simples. Parece que terceirizar para indivíduos é mais complicado que terceirizar para filiais em outros países, mas no geral o processo é basicamente o mesmo.

  • Vida como um Trabalhador Remoto - mais bom senso para qualquer pessoa: tenha disciplina, construa uma rotina balanceada, reserve tempo para sua família. Deste episódio o único capítulo que é relevante é o último: "Garanta que você não está sendo ignorado". Este é meu ponto principal para o restante do artigo, então lembrem disso.

Concluindo, dos livros que já li da 37signals, este é sem dúvida o mais fraco deles. Quando ler o índice é suficiente para entender o livro todo, não é difícil chegar a essa conclusão. Só isso não significa que está errado ou é ruim, mas um artigo de blog seria suficiente para passar a mesma mensagem: sim, existem muitas vantagens em trabalho remoto. E, claro, coincidentemente eles desenvolvem ferramentas para o que está sendo descrito no livro, portanto, é uma excelente obra de self-marketing, seria burrice não fazer isso.

A Verdade sobre o Trabalho Remoto

Agora vamos ao outro lado da moeda. No meu artigo recente Matemática, Trolls, Haters e Discussões de Internet tentei passar um conceito simples mas muito importante: fórmulas incompletas não servem para nada. Recapitulando, um procedimento, uma metodologia, um processo só podem ser "fórmulas" se elas definem o "domínio" do problema. Vou assumir que a 37signals está argumentando que profissionais da área serviços poderiam trabalhar remotamente. Restringi à computação porque diversas outras áreas simplesmente não funcionam remotamente. Pilotos de avião, bombeiros, médicos, etc. Portanto o domínio se limita a serviços - nunca produção - e cujos resultados não precisam de mídia física, portanto, Internet. Músicos, programadores, escritores, contadores, advogados, arquitetos (parcialmente), etc. Acho que isso é simples de definir. Para meus propósitos vou me restringir à área de serviços de desenvolvimento de software.

O maior problema de livros ou pessoas que evangelizam sobre comportamento humano é não ter bases na Sociologia, Psicologia e campos de estudo que já estudaram esse problema. O livro Remote é conveniente para o domínio da 37signals e funciona lá, mas da forma descrita funcionará apenas lá. Essa é a primeira coisa que você procura para definir entre uma pesquisa séria e uma auto-ajuda: "Funcionou para mim, portanto pode funcionar para você."É como se vendem dietas.

Por diversas razões - incluindo as que Zed Shaw explica nesta palestra, incluindo o aporte de capital de Jeff Bezos - a 37signals deu muito certo enquanto empresa de produtos. Bom para eles, mas significa que o domínio do problema é "empresa de produtos que fatura e lucra muito bem". Isso elimina uma parcela considerável do mercado da equação. E se você não conhece os dados, estamos falando de um universo muito maior que no Brasil representa perto de R$ 37 bilhões. Bem maior do que o universo restritivo de tech startups.

Vamos entender outra coisa que tanto Getting Real e Remote tentam argumentar - e poderiam ter simplificado. Estruturas altamente centralizadas, com alto controle top-down, pouca ou nenhuma valorização da força de trabalho, costumam ser lugares ruins de trabalhar. A conclusão errada é que descentralização completa é a melhor forma, e a conclusão mais errada ainda é imaginar a 37signals como uma empresa descentralizada por ter todos remotos, por não ter uma estrutura hierárquica dura e imexível. Na realidade é centralizada, com níveis intermediários de descentralização.

Jason Fried, DHH no centro

Dá a impressão de ser uma organização descentralizada porque se utiliza de processos de open source para a execução, mas a cadeia de comando é claramente centralizada em Jason Fried e David Hansson. Não há nenhum problema nisso, e é como deveria ser mesmo.

Dado esse entendimento, quero mostrar um gráfico que tirei da aula de Nicholas Cristakis. Ele menciona um estudo sobre os shows da Broadway. Alguns tem muito sucesso, outros são fracassos de bilheteria, e os pesquisadores queriam saber se havia alguma correlação entre o tipo de rede social da organização dos shows e as taxas de sucesso.

Considere no eixo X, a densidade de relacionamentos dos profissionais da equipe do show. 0 sendo altamente centralizada onde as pessoas no canto da rede não se falam, e 100 sendo uma rede altamente descentralizada onde qualquer um fala com qualquer um. O nível 0, altamente centralizado, por intuição, sabemos que deve ser ruim e, de fato, a correlação é que ela se associa a shows fracassados. Porém o que não é intuitivo é que redes totalmente descentralizadas demonstram o mesmo nível de fracasso. Os show de sucesso estão nas redes intermediárias, onde há alguma descentralização e alguma centralização.

O sucesso está no meio

A da 37signals não é totalmente centralizada (os programadores remotos conversam em chats, via Github, etc) e não é totalmente descentralizada (existe algum nível de especialização, e obviamente existe um comando estratégico central).

Estou experimentando diferentes cenários, e o primeiro passo - no meu caso - começa com este outro diagrama:

Codeminer 42

Mas como disse antes, não vou desviar do assunto principal neste artigo. Apenas fica a dica.

O Problema da Carreira

Estou repetindo que a definição de domínio é importante porque no caso da 37signals você pode indefinidamente aumentar o salário das pessoas porque o produto já atingiu patamar de escala onde o faturamento é ordens de grandeza superior aos custos e despesas. Oras, dá para investir em correr na Le Mans, certamente dá para pagar 5 ou 6 dígitos para os programadores.

Se continuar a evoluir o produto, criar novos produtos com cuidado, manter os clientes antigos contentes, fazer bom marketing para atrair novos, enfim, o que uma boa empresa deve fazer, será possível continuar crescendo por um bom tempo.

Só que empresas de produtos semelhantes não é comum, na verdade é o mais incomum na indústria, e as mesmas regras não se aplicam a empresas de serviço, que são a maioria. E não é por má vontade, é por fundamentos da indústria.

E disso vem o pensamento de "Por isso não é bom abrir empresas de serviços e sim ir direto pra produtos." E minha resposta é simples: "Boa sorte". A premissa óbvia desta afirmação é que empresas de produtos imediatamente dão certo. Eu discuti isso no meu artigo anterior Tech Startups, superlotação de B2C. Boring..

"Ah, basta seguir Lean Startup, processos Ágeis, Business Canvas", e todo o pacotinho que vendem atualmente, que tem instruções que qualquer criança consegue seguir e as pilhas já vem inclusas, e boom sucesso automático ... alguém realmente acredita nessas bobagens?

Agora vamos voltar à realidade. O maior problema de contratar programadores home office é um problema de Recursos Humanos, de gestão de carreira, e não somente de controle. Estamos atingindo um nível de maturidade em desenvolvimento de software, onde programar seguindo boas práticas, boas tecnologias, com eficiência, com previsibilidade, com facilidade de manutenção futura, etc, não é mais um Premium, está se tornando o normal. Eu não vou oferecer um programador mais barato a um cliente porque ele tem qualidade baixa, eu quero que meu júnior suba para pleno rápido e que o cliente tenha um nível de qualidade técnica semelhante, não importa quem faça, e que a qualidade esteja sempre acima da média - porque a média da indústria ainda é indiscutivelmente abaixo da linha da pobreza.

Só que se isso é o normal, como uma empresa pode dar aumentos a programadores? Até certo grau, a capacidade técnica faz muita diferença, mas considerando que são raros - não só na área de serviços, mas até mesmo na área de produtos - que estejamos lidando com altíssima tecnologia ainda experimental e que pouca gente tem noção do que fazer, o resto se comoditiza rápido. O mundo open source quase garante que uma vez que um problema difícil é resolvido, ele será replicado rapidamente. Se em algum momento era considerado "complicado" fazer um front-end web responsivo, com elementos visuais minimamente bonitos, um Foundation ou Bootstrap eliminaram boa parte dessa curva, por exemplo.

Este é o core da questão: apenas programar é muito pouco para 90% dos projetos de desenvolvimento de software. Discuti em outro artigo, Estimativas são Promessas. Promessas devem ser cumpridas. que a coisa mais difícil na carreira de um Engenheiro de Software é entender que muito de um projeto de software não se resolve com software. Um profissional "sênior" não é quem faz o código mais bonito, mas quem melhor sabe gerenciar as expectativas do cliente (seja ele um cliente corporativo ou cliente consumidor) e assumir responsabilidades.

Novamente, não vou me ater às exceções como centros de pesquisa, ou produtos consolidados. Ao estar dentro de um grupo emergem propriedades que só existem dentro de um grupo (novamente, vejam a palestra do Christakis ou outros como Duncan Watts para começar a entender essas propriedades). Atividades que normalmente não acontecem individualmente como treinamento peer-to-peer, onde um junior tem a oportunidade de aprender com alguém mais senior, onde habilidades de comunicação, negociação, responsabilidade, team work no geral pode ser exercitado. Tudo isso é possível estando remoto, mas é bem mais devagar e bem menos óbvio porque cada indivíduo está de fato isolado. Um cluster geográfico também tem influência no mercado e comunidade local que o cerca, então não está restrito somente ao seu grupo primário, mas grupos secundários emergem. E cada cluster se relaciona como descrito mesmo no livro Remote, com ferramentas online e eventuais viagens presenciais.

Na prática, quanto melhor um desenvolvedor sênior é mais produtivo e mais eficiente que um júnior? 10 vezes, se muito? Significa que se um júnior ganha 1000, no máximo um sênior ganha 10.000? E depois disso? Agora, um bom profissional que consegue treinar, orientar e se auto-escalar de 1 para 3, já subiu ordens de grandeza. Ele pode facilmente ir pra 20.000 ou mais dessa forma. Faça isso em clusters e em breve temos uma rede mais resiliente.

Significa então que um indivíduo trabalhando remoto é errado? Claro que não, e esse é outro problema desse tipo de argumentação, faz parecer que só existem dois caminhos. E como em tudo em sociologia, existem diversos cenários diferentes para diferentes configurações. O que as evidências indicam como os mais ineficientes são de fato os dois extremos: totalmente centralizado e totalmente decentralizado. No intervalo entre eles existe uma série de oportunidades diferentes.

Conclusão

Portanto sim, o que está no Remote não tem nada de errado. O que está errado é considerá-lo a única solução. Eu poderia contratar indivíduos isolados como home office e não teria nenhum problema com isso. Mas eu não consigo imaginar ainda formas para fazê-lo evoluir, expô-lo a mais atividades que não seja apenas codificação, e dificilmente poderia delegar mais responsabilidades que não apenas código. A grande vantagem de trabalhar no mercado de serviços é ter acesso a mais empresas e mais situações do que qualquer funcionário de apenas uma empresa ou um único produto jamais teria como ter acesso. Estando remoto, existem muito poucas oportunidades.

E novamente, existem exceções, grandes nomes do mundo open source que trabalham como freelancers, sozinhos, e estão muito bem e sua reputação só cresce. Agora devolvo com: essa é a regra ou a exceção? Exceções existem, e se possível é melhor ser a exceção. Mas seria muita irresponsabilidade evangelizar o caminho da exceção o tempo todo.

Trabalhar de casa é uma experiência que vale a pena experimentar, de maneira nenhuma ela é necessariamente melhor que trabalhar dentro de um grupo, mesmo considerando "perder tempo" de locomoção - que, aliás, é uma péssima desculpa.

Recuperando os vídeos do blog (Dropbox + JW Player) - Parte 2

$
0
0

No post anterior listei os posts com embeds de vídeo atualizados. Aqui estão os vídeos avulsos que utilizei em diversas palestras que apresentei nos últimos 5 anos:

Este foi um dos vídeos que mais usei para descrever alguns abstratos a respeito de Rails e sua sinergia com produtos web.

Este clássico de Carl Sagan é uma explicação sobre evolução biológica e seleção natural. Eu usei como paralelo para explicar o conceito de seleção natural e como códigos open source passam por um processo semelhante de evolução e seleção natural/artificial. Os conceitos são os mesmos: mutações aleatórias (contribuições voluntárias), seleção artificial (committer), seleção natural (distribuição gratuita, feedback de usuários, exploits de segurança), melhoria cumulativa (repositórios abertos versionados, distribuição gratuita).

[OBSOLETO] Quando Node.js ainda estava surgindo, surgiu um experimento de framework para aplicações assíncronas chamada Cramp. Não há novos commits faz pelo menos 2 anos, então considero que é obsoleto. Mantenho o vídeo apenas para posteridade.

[DESATUALIZADO] Utilizei estes dois vídeos acima para mostrar como se otimiza muito o tempo de resposta deferindo tarefas que demoram para background, na época usando Resque. Não há nada de errado em ainda se usar Resque e muitos usam, mas temos uma opção muito melhor com Sidekiq. Sem falar que usei Paperclip, e hoje preferimos mais o Carrierwave, apesar dos dois serem usados. E, obviamente, era Rails 2.3, hoje estamos na versão 4.0.1.

[DESATUALIZADO] Tudo no mundo Web de Ruby utiliza Rack como mediador entre frameworks e servidores. Você pode inclusive criar uma aplicação (simples) usando puramente Rack, sem depender de frameworks se quiser. Vale a pena aprender esse básico. Novamente, existem novidades no Rack desde então mas os conceitos básicos são os mesmos.

[DESATUALIZADO] Sinatra continua na ativa, ganhou um irmão "assíncrono" chamado Async Sinatra mas no geral ainda tem os mesmos princípios e os mesmos propósitos: aplicações web pequenas, principalmente APIs Web. Este vídeo é antigo, existem muitas novidades, mas o básico ainda deve se aplicar.

Este vídeo era para ser uma brincadeira em palestras que dei sobre Ruby. Quando explicava sobre "metaprogramação" eu fazia eu mesmo explicar (eu real falando comigo mesmo em vídeo). De qualquer forma, se você é iniciante este vídeo ainda deve ser útil explicando os conceitos de objetos e introdução à metaprogramação do Ruby.

Esta é uma das minhas palestras favoritas, meu tema pelo ano de 2009 principalmente. Nela tento demonstrar porque o comportamento humano é tão "estranho" e alguns dos mecanismos já descobertos e descritos por áreas como sociologia. Recomendo muito assistir.

Fui muito influenciado pelas pesquisas no campo de Redes, em particular sobre os assuntos que cruzam áreas como "Emergência". Usei muito este trecho do TED de Strogatz e em diversas palestras entre 2008 e 2009 eu realizei o experimento de bater palmas (que tem na versão original deste vídeo) para demonstrar como comportamentos aleatórios podem emergir e se sincronizar sem planejamento prévio.

Finalmente, prefiro evitar vídeos motivacionais - considero o campo todo de "motivação" muito apelativo e superficial - mas este vídeo em particular é muito bem feito, acho que ainda vale a pena divulgar.

Ufa! E isso termina minha enorme varredura em busca dos vídeos perdidos. Se você ainda encontrar algum post meu que tenha vídeos quebrados, por favor não deixe de avisar nos comentários.


Recuperando os vídeos do blog (Dropbox + JW Player) - Parte 1

$
0
0

Hospedar vídeos para mostrar aqui no blog e para arquivar vídeos que usei em palestrar tem sido uma novela. Por volta de 2008, YouTube ainda não era tão dominante, ainda existia Google Videos, e alguns vídeos cheguei a colocar lá primeiro. No começo, se não em engano, YouTube ainda tinha limite para upload, políticas mais restritivas. Então pulei direto para o Vimeo. Por algum tempo ele serviu bem, mas um belo dia eles mudaram a politica e, sem aviso nenhum, cortaram minha conta e tuod que eu tinha nela. Aliás, muito péssimo esse jeito de tratar usuários. Tentei outras formas alternativas como Vevo, Rapidshare, mas nada satisfatório. A partir de 2010 mais ou menos migrei para o Blip.tv, para a conta paga. Imaginei que se eu pagasse estaria menos propenso a me cortarem como fez o Vimeo. Ledo engano, este ano eles mudaram as políticas deles, e me mandaram uma notificação do tipo "você tem até dia X para tirar tudo porque vamos fechar sua conta". Novamente, o que acontece com esses serviços? Péssimo serviço aos usuários é uma constante.

Finalmente, depois de pensar a respeito, resolvi jogar tudo numa pasta de Dropbox da minha conta paga, e usar JW Player para fazer o embed. Depois de esperar um bocado para o upload de tudo terminar (3.78GB e 65 arquivos de vídeo), e mais algumas horas para vascular todos os meus posts e editar um a um para substituir os embeds antigos, acho que consegui atualizar praticamente tudo.

São anos de posts, alguns deles já são sobre assuntos que hoje são obsoletos, mas mesmo assim atualizei para manter os registros históricos só que não vou listar neste post. E para quem estiver curioso em assistir os vídeos novamente, aqui vai uma listagem dos posts atualizados em ordem cronológica:

No próximo post listarei os vídeos que não estão associados a nenhum post do blog.

Rubyconf 2013 - Lightning Talks

$
0
0

Estou um pouco atrasado, mas para quem não sabia recentemente aconteceu o Rubyconf 2013 em Miami. Todas as palestras estão gravadas e disponíveis pela excelente Confreaks.

Estava assistindo as Lightning Talks que foi bem longa (mais de 2 horas!) e selecionei as que foram realmente práticas. Veja minhas favoritas na lista a seguir. Clique no link do tema para ir direto na posição certa no vídeo do YouTube˜ para não perder tempo.

Appraisal - esta gem é muito interessante. Quem já não fez ou usou uma gem que tem dependências externas, por exemplo compatível com activerecord 3.0.0, e agora você está fazendo um projeto em Rails 4 e a gem não vai funcionar? Às vezes é só uma questão de atualizar a dependência, rodar as specs e elas vão passar ou fazer ajustes pequenos. Mas para manter a compatibilidade com múltiplas versões de gems, pode ser tedioso ficar testando múltiplas versões da sua gem. E para isso a gem Appraisal pode ajudar. Vale a pena dar uma olhada.

Uma crítica é que o @sikachu é um desenvolvedor excepcional, mas é um péssimo comunicador. É doloroso assistir a apresentação. Lembrem-se: comunicação não é apenas 'dizer' é fazer o outro lado se engajar. Por favor, dediquem-se a se comunicar melhor.

GC.disable - o grande @Nari3, um dos contribuintes do Ruby MRI e criador do atual bitmap garbage collector do Ruby 1.9.3 e 2.0 fez uma das melhores apresentações que já vi que demonstra visualmente o que significa quando o garbage collector "stop the world" para realizar uma coleta, que é o comportamento do GC do Ruby até a versão 2.0. A partir da versão 2.1 entrará o novo generational garbage collector ('semi') do grande Koichi Sasada e, visualmente, o Nari demonstra qual é a grande diferença. Novamente, assista.

Semantic Versioning - este assunto não é nada novo, mas por isso mesmo é bom relembrar já que muita gente faz muito errado ainda: colocar versões corretas nos seus projetos, declarar corretamente a dependência de versão de coisas externas. Mesmo projetos famosos (cof Sidekiq cof) ainda fazem errado.

PolyTEXnic - muitos devem conhecer o @Mhartl como autor do Ruby on Rails Tutorial (aliás, excelente material para iniciantes). Ele criou um conjunto de ferramentas para gerar não só sites mas ebooks e pdfs bem diagramados, com boa tipografia, e tudo mais que é bom do TeX. Vale a pena ver como ele deixou todo o processo muito simples.

Ruby Logger Thread/Process Safe - Log é algo normalmente ignorado por muitos, mas é um aspecto que está aumentando em escopo mais e mais, especialmente com novas soluções como Greylog2, Logstach e o Fluentd do @sonots, que faz esta palestra. Ele discute alguns aspectos mais low level da linguagem Ruby e garante que o logger do Ruby esteja thread-safe e process-safe (afinal o Ruby faz fork, e o log não pode se perder com isso).

Aliás, vários japoneses da comunidade Ruby do Japão palestraram nas Lightning Talks. Japonês não sabe falar inglês, ponto. #vergonhaalheia

Unbelievable - Essa do Michael Dvorkin não vou nem descrever. Na prática não tem utilidade nenhuma, mas foi tão divertido o que ele fez que quis compartilhar. Assista.

Em termos das apresentações, a grande maioria é bem ruim (conteúdo até bom, mas apresentação ruim). Muito gaguejo, muita quebra de raciocínio, piadas sem graça. Pratiquem, apresentar não é tão simples quanto parece. As que foram boas são justamente de quem já tem prática, veja o do CTO da RapGenius por exemplo.

Coloco esse comentário porque muitos acreditam na visão romântica de que o exterior (apresentação) não é importante, o que realmente importa é o interior (conteúdo). Sinto muito, uma coisa não elimina a outra: se a embalagem não me atrai eu não compro o produto, e aí perco a oportunidade de ver o que 'poderia' ser um excelente conteúdo. Só embalagem e um conteúdo decepcionante também não ajuda. Logo a conclusão é óbvia: ambas as coisas são importantes, apresentação e conteúdo.

Rails Assets

$
0
0

Ontem o @Fgrehm mandou no nosso Campfire sobre o projeto rails-assets.org. Ele acabou de ser lançado, portanto não pode ser considerado mais que um "beta". Ela pode ser tanto a salvação da lavoura para front-ends em projetos Rails ou não, na prática ainda não sabemos.

O @jcemer publicou hoje um ótimo post explicando isso do ponto de vista de um front-end full-time, colocando em perspectiva.

Em resumo, o Rails trás um mecanismo fantástico chamado Asset Pipeline. Ele tem diversas funções, em particular otimizar os assets dos projetos Rails, compilando fontes de Sass ou Less para CSS. Depois concatenando todos os CSS, JS em um único arquivo. Depois minificando (com Uglify) para diminuir seu tamanho. Em particular com Sass ele ainda pode concatenar sprites em uma única imagem também. Enfim, o pacote completo para otimizar assets ao máximo.

Se você ainda não conhece sobre esse recurso, não deixe de ler meu tutorial.

Só que à medida que um projeto precisa ter várias bibliotecas CSS, JS, etc o processo pode ficar chato. Principalmente porque em alguns casos precisa manualmente modificar os paths dentro de seus fontes.

Um dos problemas é que o Asset Pipeline vai juntar todos os arquivos em um único, colocando timestamp no nome. E aí os paths não vão funcionar a menos que usem o helper asset_path ou asset_url. Por isso acabamos usando rubygems feitas manualmente como o bootstrap-rails.

E isso leva a outro problema: atualizações. Primeiro, os autores originais lançam uma determinada biblioteca Javascript. Podemos manualmente fazer o download dos fontes e "vendorizá-los" no projeto. Ou, se você utilizar o Bower podemos esperar sair esse novo pacote e atualizar com o Bower. Ou, se estivermos usando uma Rubygem, podemos esperar sair a nova gem e atualizar com Bundler.

Então, vamos separar tudo e usar Bower pra gerenciar bibliotecas de front-end e usar Bundler para gerenciar gems de back-end e todo mundo fica feliz? Não é tão fácil assim já que muitas gems - ActiveAdmin como um bom exemplo - já dependem de bibliotecas de front-end encapsuladas em rubygems como temos neste trecho da gemspec:

12345
...
s.add_dependency "bourbon"
s.add_dependency "jquery-rails"
s.add_dependency "jquery-ui-rails"
...

Já que temos duas comunidades tendo exatamente o mesmo trabalho: no Bower alguém vai atualizar pacotes de jquery; na Rubygems alguém vai ter que atualizar também. Então, porque não otimizar isso e apenas um deles atualizar. Nesse caso vamos aproveitar - e colaborar - para deixar as do Bower sempre atualizadas.

Será que existe uma forma, então, de converter um pacote Bower para um pacote Rubygems? E este é o problema que o Rails Assets se propõe a tentar resolver. Seguindo exatamente o mesmo exemplo do site deles, colocaremos na Gemfile de nossos projetos Rails desta forma:

12345678910111213
source 'https://rubygems.org'
source 'https://rails-assets.org'

gem 'rails'

group :assetsdo
  gem 'sass-rails'
  gem 'uglifier'
  gem 'coffee-rails'
  gem 'rails-assets-bootstrap'
  gem 'rails-assets-angular'
  gem 'rails-assets-leaflet'end

A primeira diferença é um novo source. E isso é importante pois não vai poluir o repositório de Rubygems com gems que são meros 'stubs'.

A segunda diferença são as gems prefixadas seguindo o padrão "rails-assets-[nome do pacote Bower]" que, obviamente, foram feitas desta forma para não conflitarem com as que já existem no Rubygems.org. Se isso funcionar significa que a antiga gem "bootstrap-rails" poderá ser descontinuada e passamos a usar a "rails-assets-bootstrap", e assim por diante.

Se uma determinada gem ainda não existir, o processo vai demorar um pouco porque o Rails Assets vai baixar o pacote do Bower, modificá-la, encapsulá-la como uma Rubygem e publicar no seu repositório. Mas quanto mais gente usar com mais bibliotecas, mais rubygems já estarão prontas para baixar imediatamente.

Para o Asset Pipeline continua a mesma coisa. No mesmo arquivo app/assets/javascripts/application.js teríamos igual:

123456
//= require_self//= require bootstrap//= require angular//= require leaflet//= require_tree .//= require_tree shared

E no app/assets/stylesheets/application.css teríamos também igual:

1234567
/*
 *= require_self
 *= require bootstrap
 *= require leaflet
 *= require_tree .
 *= require_tree shared
 */

As rubygems que encapsulam bibliotecas front-end não são apenas por causa do Asset Pipeline, além disso elas também podem conter generators para facilitar a criação de alguma estrutura de diretórios ou arquivos de configuração específicos. Mas mesmo nesse caso podemos separar em duas coisas: uma sendo só os fontes originais e uma segunda gem que depende da primeira com ferramentas ou outros extras como generators.

Conclusão

Para Rubistas a primeira vantagem mais óbvia é o seguinte: não precisamos misturar dois workflows de dependência, uma com Bower (ou manualmente) e outra com Bundler. Podemos continuar apenas com Bundler. Lembrando que se pacotes Bower não forem vendorizados no projeto Rails ainda teríamos que manter esse processo com Bower na integração contínua - adicionando o comando no .travis.yml, por exemplo; e também teríamos que ter no deployment. Idealmente, não deveríamos ter que vendorizar bibliotecas. Isso sem contar que se usar Bower num projeto Rails e usar Heroku, precisa de um outro buildpack para funcionar. Ou seja, o Bower adiciona muito mais complexidade do que somente ter que rodar mais um comando "install" em desenvolvimento. Ambientes de teste, produção, tudo fica um pouco mais complicado, e desnecessariamente.

Além disso, quem colaborava mantendo a rubygems de um "bootstrap-rails", por exemplo, pode continuar a dedicar seu tempo atualizando diretamente o pacote equivalente do Bower e deixar essa fonte única sempre atualizada, o que pode ser um reforço ao Bower e aos não-rubistas que a utilizam.

E com isso também não precisamos ter gems de stubs poluindo o Rubygems.org. Ou seja, "se" funcionar será uma situação ganha-ganha para todo mundo. Porém nem tudo são flores. Como uma das idéias do Rails Assets é modificar o fonte para adicionar os asset-paths corretos, quer dizer que podem existir situações onde patterns automáticos não peguem, ou existam ambiguidades que precisem ser resolvidos manualmente. Então, das duas uma: ou colaboramos com o Rails Assets para adicionar os patterns que faltam, se for possível; ou criamos um passo no workflow do Rails Assets que permitam modificações manuais. Ainda não sei, precisamos ver quais são esses cenários e como corrigí-los. Mas isso não elimina os pontos positivos descritos anteriormente.

O melhor a fazer: testar e experimentar. O projeto é open source, você pode colaborar mandando Pull Requests ou mesmo reportando Issues e ajudando a evoluir. Se a idéia for furada - o que não me parece - vamos saber rapidamente - e voltamos ao mesmo problema de antes, mas pelo menos esta é uma proposta de solução muito interessante que vale a pena ser explorada mais a fundo.

Palestras de 2013

$
0
0

Durante este ano participei de diversos eventos, incluindo semanas de computação em diversas faculdades pelo Brasil.

Dentre workshops, palestras de introdução ao mundo Ruby, dois temas em particular foram destaque para mim.

O primeiro é o tema que chamei de "Startups & Software". Ela não deixa de ser uma evolução de outras palestras que fiz anos atrás como o Entenda Software da Forma Correta de 2010. A intenção é demonstrar que o raciocínio entre desenvolvimento de software, criar startups de sucesso, gerar open source de sucesso, seguem os mesmos princípios. Os dois princípios básicos sendo: "lei dos grandes números" e meu conceito pessoal que verdadeira inovação nasce de restriçoes. Assista a versão mais recente dessa palestra que apresentei online pela Eventials:

O segundo tema que quase todo ano eu atualizo é sobre o Ecossistema Ruby e Rails. Esta versão qeu apresentei no evento Rupyé um pouco diferente. Ela cruza alguns dos acontecimentos da comunidade Ruby com acontecimentos gerais do mundo da tecnologia. A intenção é demonstrar porque uma comunidade como a Ruby, que praticamente saiu do zero em 2004 foi capaz de gerar um ecossistema sustentável. Quais são as características que tornaram isso possível e, a partir de todas as restrições, porque ela se moldou da forma como é hoje. Isso deve ajudar a entender para onde ela está indo a partir de agora:

O terceiro eu acabei de gravar via Eventials e é sobre porque você não deve fazer search usando SQL do tipo SELECT LIKE. Entenda as melhores opções, Solr, Elasticsearch e como elas são tão simples que não vale nem a pena considerar usar SQL LIKE:

Aproveitem!

Soluções para um Mundo Assíncrono/Concorrente

$
0
0

Se você é minimamente informado sobre os últimos desenvolvimentos no mundo de linguagens e frameworks web, vai lembrar que algumas das coisas mais discutidas são sobre o fator "concorrência", "paralelismo", "I/O assíncrono". Todos os termos são associados com ser ou não ser "escalável".

Nesse cenário, vamos lembrar que nem Ruby, nem o framework Rails se encaixam nesses termos. Por isso muitos chegam à conclusão que é hora de ir para Node.js, Go, Elixir, Scala, Clojure.

Como eu repito essa mesma resposta faz algum tempo, vou descrever aqui em linhas gerais. Para 90% de nós, programadores de aplicações web, isso não é necessário e sequer é prático.

Uma requisição web, quando chega ao servidor e à sua aplicação, precisa executar muita coisa até terminar de montar o HTML de resposta. Esse tempo tem que ser o menor possível para que o mesmo servidor possa responder o máximo possível por período de tempo (throughput).

Em linhas gerais, se uma requisição demora porque precisa ficar esperando operações de leitura/escrita (ou, "I/O"), por exemplo escrever no banco, uma query pesada, uma chamada de web service pela internet, etc, dizemos que essa requisição é "I/O bound", limitada por I/O.

Por outro lado se o que mais faz a requisição demorar é o processamento em si, por exemplo processar um array de objetos que retornou do banco, transformando-a em outra estrutura, como o HTML de resposta, ou gerar um PDF, ou qualquer coisa que seja limitada por CPU, dizemos que a requisição é "CPU bound".

Não é uma coisa "binária", ou é I/O bound ou é CPU bound, mas é uma forma de descrever em linhas gerais qual é o gargalo principal. No caso de CPU bound, por exemplo, não tem jeito, precisa ter ou CPUs mais rápidas, ou várias CPUs em paralelo e daí sua aplicação suportar ou multithread ou multi-processos para utilizar todas as CPUs.

No caso de I/O o sistema operacional precisa suportar I/O assíncrono (e na prática, todos suportam hoje). No caso de um Linux, você terá chamadas de notificações de eventos de I/O de baixo nível chamado epoll. Todo mundo vai usar isso para ter o recurso de I/O assíncrono, e isso inclui Python, Ruby (sim, Ruby também suporta), Node.js, Java (NIO), etc. Não há nada específico de uma linguagem que contribua para isso: o OS suporta ou não.

Portanto, a lição número 1 é que tanto problemas de CPU bound e I/O bound são conhecidos e resolvidos faz anos. Não há novidades aqui.

Porém a novidade é que agora algumas linguagens tem sido criadas primariamente para tentar criar abstrações mais confortáveis para esses problemas. Daí surgem coisas como Actor Model , conceitos que já existiam como "estado não compartilhado". Em particular, no mundo Ruby você vai encontrar os melhores avanços desse modelo no projeto Celluloid, cujo principal caso de uso é o sistema de filas assíncrono Sidekiq. Use o Amazon AWS SQS se quiser algo realmente robusto.

Aliás, tudo que é CPU bound, deve ao máximo ser jogado para servidores de fila para processamento paralelo em background, sem segurar o tempo de resposta de uma requisição. Processamento de imagens, processamento de dados, conexões com web services, geração de relatórios, indexação de dados, tudo isso deve ser trabalho para jobs em background. Faça sua aplicação web gravar a tarefa na fila e configure workers para executá-los. Se você usar Heroku, por exemplo, ele já tem dynos específicos para workers. Configurar o Sidekiq no Herokué quase trivial.

Fazendo isso é possível literalmente cortar segundos inteiros de requisições web lentas e jogar tudo para tarefas assíncronas. É assim que se começa a resolver processos intensivos de CPU na web. A segunda etapa, quando necessário, é migrar para JRuby, isso resolve quase todo o resto dos outros problemas.

No caso de um Node.js, por exemplo, o principal caso de uso para ele sempre foi um socket.io, implementar servidor que serve HTML 5 WebSockets que, por sua natureza, são conexões que não se fecham rápido. E isso é justamente um problema para ser resolvido com I/O assíncrono. Eu sei, eu sei, tem muito mais que se pode fazer com um Node.js, mas na prática esse era um grande diferencial comparado aos outros.

Para a maioria de nós - isso já foi resolvido de forma mais simples, como serviços. Enviar e receber notificações, manter conexões persistentes em altas quantidades (milhares ou milhões), pode ser totalmente resolvido simplesmente contratando serviços por preços tão baixos quanto USD 1 por milhão de mensagens e 10 mil conexões simultâneas na Realtime.co. E além dele você ainda pode escolher entre o Pusher e o PubNub. Esse caso de uso pode ser estendido para os casos de chat, notificações em mobile, notificações real-time na web, etc. Esqueça a idéia de montar sua própria máquina de Node no EC2 para isso. Simplesmente não precisa.

Eu repito "para a maioria de nós" porque não estou considerando os casos excepcionais onde você trabalha com infraestrutura especial, que atende milhões ou dezenas de milhões de pessoas, onde estamos falando de centenas de milhares de conexões simultâneas, bilhões de requisições por mês. Se você não lida com esses números, faz parte da "maioria de nós". E a maioria de nós tem soluções simples hoje em dia. Olhe primeiro na Amazon AWS antes de sequer pensar em fazer algo do zero. Certamente seu problema se resolve com Elastic Beanstalk, OpsWorks, RDS, SQS, SES, DynamoDB, Elasticache, etc.

E por isso mesmo, ecossistemas como o Rails continuam fortes e crescendo: porque o que muitos ainda estão batendo cabeça para resolver (Asset Pipeline, por exemplo), nós já temos resolvido faz tempo. Não temos mais muitas dúvidas quanto a processos de deployment, ciclo de vida de projetos, melhores padrões de desenvolvimento, melhores boas práticas, etc.

A intenção deste artigo é mais dar uma referência a quem está iniciando ou tem pouca experiência em Ruby e Rails, pois superficialmente pode parecer que há algo com o que se preocupar que na verdade já está resolvido. As coisas continuam tão escaláveis ou mais do que sempre foram, não há nenhuma grande novidade hoje em dia, os casos de uso não mudaram e nem suas soluções. Antes de novos use cases aparecerem, a maioria das coisas que vemos hoje em dia são soluções procurando um problema para resolver.

Top 15 Artigos de 2013

$
0
0

O que tem de novo no Ruby 2.1?

$
0
0

Feliz Ano Novo. Vamos ao primeiro post de 2014 :-)

Conforme prometido, o Ruby Core Team nos entregou seu presente de Natal no dia 25/12/2013. A versão 2.1 do Ruby foi lançado oficialmente.

Se usa RVM basta instalar com rvm install ruby-2.1.0.

Se usa rbenv com ruby-build basta instalar com rbenv install 2.1.0.

A versão TL;DR é o seguinte: se o que você está programando funciona com Ruby 1.9.3 ou com Ruby 2.0.0, muito provavelmente vai funcionar com Ruby 2.1.0. Cada uma delas tem novas funcionalidades em relação à anterior, mas elas não quebram a compatibilidade a ponto de quebrar.

Se sua aplicação utiliza Bundler modifique sua Gemfile para ter a seguinte linha:

1
ruby '2.1.0'

Eu utilizo RVM e quando entro num projeto com uma Gemfile já associada a um Ruby ele automaticamente escolhe a versão certa. Ou mude manualmente como rvm 2.1.0 ou rbenv local 2.1.0. A partir disso rode o bom e velho comando bundle para instalar as gems novamente nesta nova versão de Ruby. O resto é a mesma coisa. Se tudo der certo, você não deve notar nada de diferente.

De Ruby 1.9 para 2.0 para 2.1

Se você ainda utiliza Ruby 1.8, saiba que ele já é obsoleto e não recebe mais manutenções nem correções de segurança. Você já deveria ter mudado para Ruby 1.9 ou 2.0 meses atrás. Não há desculpas para não fazê-lo. As desculpas são as mesmas que perpetuam abominações como Windows XP ou Internet Explorer 6.0 até hoje.

Em Maio eu fiz um artigo chamado Indo de Ruby 1.8 e Rails 2.3 para Ruby 2.0 e Rails 3.2, não deixe de estudar com cuidado para migrar o quanto antes.

Do 1.8 para 1.9, em resumo:

  • Muito mais performance, especialmente a partir da 1.9.3 que é uma ordem de grandeza mais rápido
  • Mudança para o YARV a máquina virtual baseada em bytecode por Koichi Sasada
  • Suporte a Fibers, uma espécie de corotina, uma unidade de processamento que pode ser interrompido e manualmente recomeçado
  • Suporte a Encoding e Unicode, com isso ficou fácil internacionalizar
  • Integração do Rubygems à Linguagem
  • Adição da nova sintaxe de hash de { :a => 1 } para { a: 1 }

Do 1.9 para 2.0, em resumo teve poucas coisas também:

  • Keyword arguments, def foo(param1: 1, param2: 2); end
  • Module Prepend - achei que mais gente usaria isso em vez de include mas ainda não vi tanto uso
  • Lazy Enumerators - novamente, achei que veria mais uso, mas no mundo real não usamos coleções tão longas, ou já sabemos a sub-seção que queremos
  • Refinements - esta seria a "maior" nova funcionalidade no Ruby 2.0, mas ela nasceu sem consenso e com frustrações em seu uso. O Ruby 2.1 trás uma evolução nessa implementação.

Do 2.0 para 2.1, em resumo também tivemos poucas mudanças (veja que isso é algo bom, pois todos podemos mover rápido pra nova versão):

  • Refinements - deixa de ser experimental e pode ser aplicado dentro de um Module também
  • Decimal Literals - assim como no 1.9 números complexos ganharam uma forma literal com Complex(3,4) == 3 + 4.im agora podemos fazer 0.3r para denotar um número racional
  • Faster Numbers - Integers de 128bits e uso do GNU Multiple Precision Arithmetic Library, vai ajudar a criar bibliotecas científicas
  • Method Cache - este é um velho problema sobre invalidação de cache de métodos, principalmente quando mudamos os ancestrais com monkey patching. James Golick propôs um patch para 1.9 que finalmente evoluiu para o 2.1. Mas a atual versão já pode ser até 10% mais rápido.
  • RGenGC - esta é de longe a atualização mais empolgante. É um novo Garbage Collector, Generational, que promete mais performance e melhor uso de memória, bloqueando a virtual machine menos vezes e garantindo um processamento mais estável. Esta pode ser a semente para uma melhoria geral no Ruby.

Este benchmarké de Maio mas já dá indicativos em testes sintéticos que o 2.1 pode ser cerca de 20% mais rápido que o 2.0 e até 30% mais rápido que o 1.9.3, na média, e muito mais rápido em testes individuais.

Como podem ver, a maioria das coisas que mudam na linguagem são "opt-in", ou seja, você pode escolher usar ou não. E se não usar é praticamente como o Ruby 1.9. Por isso tanto o 2.0 quanto o 2.1 são praticamente atualizações "drop-in", ou seja, simplesmente atualize, rode sua suíte de testes, e faça deployment.

E falando em deployment, com parte do Ruby Core Team trabalhando para o Heroku, não é de se espantar que o Heroku já suporta 2.1 e para rodar seus testes o Travis-CI também já suporta o 2.1.0.

Materiais

Se quiser rapidamente aprender o que apareceu de novo entre as versões 1.8 e 1.9 e depois da 1.9 para a 2.0, compre os screencasts do Peter Cooper.

Se ainda é iniciante no Ruby, livros feitos para a época do 1.9 ainda são válidos. Em particular, recomendo o Eloquent Ruby do Russ Olsen.

Outra excelente fonte de idiossincracias do Ruby é o Ruby Tapas do Avdi Grimm. Assine e acompanhe. E um ótimo curso para quem quer iniciar em Ruby on Rails continua sendo a trilha de Ruby do CodeSchool.com.

Finalmente, entender como o Ruby funciona internamente é um estudo muito interessante e o melhor livro para isso é o Ruby Under a Microscope do Pat Shaughnessy. Vale a pena comprar.

Enfim, existem diversas grandes fontes para se aprender


[Criptografia] Não use TripleDES/ECB - e uma curiosidade sobre Cipher Key do .Net

$
0
0

Recentemente num de nossos projetos tivemos que lidar com uma integração de dados vindo de um sistema feito em C#. Até aqui nenhum problema. O código que tivemos que usar como referência, vindo de um parceiro de nosso cliente, foi basicamente este:

1234567891011121314151617181920212223242526272829303132333435
using System;
using System.Security.Cryptography;
using System.Text;

class Program
{
        public staticvoid Main(String[] args) {
                Console.WriteLine(EncryptData("hello world"));
        }

        public static string EncryptData(string Message)
        {
            byte[] Results;
            System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();
            MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider();
            byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes("abc123"));

            TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();
            TDESAlgorithm.Key = TDESKey;
            TDESAlgorithm.Mode = CipherMode.ECB;
            TDESAlgorithm.Padding = PaddingMode.PKCS7;
            byte[] DataToEncrypt = UTF8.GetBytes(Message);
            try
            {
                ICryptoTransform Encryptor = TDESAlgorithm.CreateEncryptor();
                Results = Encryptor.TransformFinalBlock(DataToEncrypt, 0, DataToEncrypt.Length);
            }
            finally
            {                
                TDESAlgorithm.Clear();
                HashProvider.Clear();
            }return Convert.ToBase64String(Results);
        }
}

Não estou quebrando confidencialidade simplesmente porque este é um código publicamente conhecido disponível no site CodeProject, sob licença. CPOL. O que eu vi foi uma cópia exata disso. Mas cuidado: grandes empresas, em grandes sistemas usados por milhões de pessoas usam código exatamente como este. (#MEDO)

O ponto de atenção é que este exemplo tenta ser o mais simples possível. Por isso ele escolhe TripleDES - que é o DES aplicado 3 vezes pra cada bloco de dados -, um dos algoritmos mais antigos e mais simples, em vez de usar algo mais moderno como Rijndael/AES. Pior ainda, TripleDES não seria tão ruim se fosse usado no modo CBC em vez do modo ECB.

Falando em termos de leigo, a diferença é que o modo CBC (Cipher Block Chaining) exige o uso de um Initialization Vector (IV) além da chave de encriptação. Diferente da chave - que deve ser "secreta" - o IV pode ser público e transmitido remotamente. O modo CBC vai usar esses dois componentes para fazer transformações em cadeia nos dados, adicionando uma camada extra de segurança.

No modo ECB (Electronic Code Book) você só precisa da chave - e por isso todo mundo usa TripleDES em modo ECB para exemplos e tutoriais: porque é mais simples - e aqui vai uma crítica para tutoriais que simplificam demais sem explicar as implicações, especialmente de segurança (!). O modo ECB é considerado inseguro.

Não sou um especialista em segurança, mas em termos leigos o mesmo dado passado pelo TripleDES com a mesma chave gera a mesma saída encriptada. Portanto se eu souber a entrada e saída de alguns dados, posso encontrar padrões que ajudem a decriptar outros dados, e impede o uso de ataques baseados em dicionários e rainbow tables. Como um IV novo é gerado para cada vez que encripto no modo CBC (importante: sempre gere um novo IV aleatoriamente - tem métodos pra isso, não reuse IVs), o mesmo dado de entrada não gera duas saída iguais, dificultando muito encontrar padrões que ajudem a quebrar outros dados. É a mesma razão de porque usamos "salts" ao gerar digests de senhas antes de armazenar numa tabela de banco de dados. Esta resposta no StackExchange descreve melhor.

Portanto, se possível, use um algoritmo decente como AES-256, como neste exemplo. E se for usar TripleDES, pelo menos evite ECB e vá para CBC, mesmo com o trabalho extra de precisar de um IV.

Aliás, se puder também evite MD5 ou SHA1 para gerar digests de senhas. Eles são algoritmos "rápidos", quebráveis com rainbow tables e força bruta. Por isso hoje usamos algoritmos que são computacionalmente "caros" (demorados) como bcrypt. MD5 e SHA1 são bons pra checar integridade de um download, por exemplo, e isso tem que ser rápido. Mas para evitar força bruta, use um demorado para o digest de senhas.

A Curiosidade: MD5 da Cipher Key (passphrase)

Como disse antes, independente da qualidade do código original, precisávamos fazer um em Ruby que gerasse o mesmo resultado. A "tradução" do código C# anterior em Ruby seria assim (versão simplificada):

1234567891011
require 'rubygems'
require 'openssl'
require 'digest/md5'
require 'base64'defencrypt_data(passphrase, message)
  cipher = OpenSSL::Cipher.new('des-ede3')
  cipher.encrypt
  cipher.key = Digest::MD5.digest(passphrase)Base64.encode64(cipher.update(message) + cipher.final)end

É só isso mesmo. Vamos por partes.

  • Não deixe de ler a documentação do OpenSSL, ele explica bem as coisas que vou dizer a seguir.
  • Pra escolher TripleDES em modo ECB basta instanciar com "des-ede3", pra ser CBC seria "des-ede3-cbc" ou apenas "des3" (alias)
  • Por padrão o padding é PKCS7, então não precisa especificar.
  • Sempre chame o método #encrypt antes de configurar a chave.
  • Para pegar o resultado precisa chamar o método #update antes e concatenar com #final.
  • Passamos o resultado por Base64 porque ele é binário, se quisermos uma string precisa converter.

Se tentar rodar este método ele vai dar o seguinte problema:

1234
> encrypt_data("abc123", "hello world")
OpenSSL::Cipher::CipherError: key length too short
        from (irb):17:in `key='
        from (irb):17:in `encrypt_data'

Se passar a mesma cipher key e mensagem pra versão .Net ele vai funcionar. Esta é a curiosidade:

  • Tanto a implementação .Net quanto Ruby esperam por padrão uma chave de 24-bytes (192-bits)
  • Todo digest MD5 tem 16-bytes de tamanho (128-bits)

No caso do Ruby, como estou passando uma chave menor que o padrão, ele estoura com o erro acima. Já o .Net faz outra coisa: ele acrescenta os 8-bytes que faltam. O problema é com o que.

Especificamente no .Net ele complementa os 8-bytes restantes com os 8-bytes iniciais do que é passado. Se fosse plain-text, por exemplo, e a chave passada fosse "1111111122222222", internamente ele converteria para "111111112222222211111111". Isso é dependente de implementação, no caso de PHP, se não estou enganado, ele complementa os 8-bytes restantes com nulo ou zero.

Por isso, pro método em Ruby ficar correto, precisamos fazer assim:

123456789101112
require 'rubygems'
require 'openssl'
require 'digest/md5'
require 'base64'defencrypt_data(passphrase, message)
  digest = Digest::MD5.digest(passphrase)
  cipher = OpenSSL::Cipher.new('des-ede3')
  cipher.encrypt
  cipher.key = digest + digest[0..7] # <= eis o truqueBase64.encode64(cipher.update(message) + cipher.final)end

Feito isso, o resultado agora será o mesmo do código em C#:

12
> encrypt_data("abc123", "hello world")
 => "90v60JwFNH+VuIKJgSVWUw==\n"

Para comparar, basta compilar e executar na sua máquina (se por acaso for um dev Windows) ou, se não for, ir no site Compile Online que permite compilar e executar código em diversas linguagens diferentes diretamente na Web (dica do @_carloslopes).

Em resumo:

  • Só porque está encriptado não quer dizer "seguro";
  • Entenda o que está copiando, não apenas copie;
  • Não use TripleDES, prefira AES;
  • Se for usar TripleDES, prefira CBC sobre ECB;
  • NÃO USE MD5, ou mesmo SHA1 para hashing de senhas, use bcrypt ou outra coisa mais moderna;
  • O módulo OpenSSL do Ruby vai conseguir replicar praticamente todo código criptografia de outras linguagens, facilitando integrações, mas existem pequenas diferenças a tomar cuidado.

CodeClimate, Qualidade de Código e os Rubistas Sádicos

$
0
0

Análise estática de qualidade de código executado automaticamente é uma daquelas coisas que quando você se acostuma a usar passa a pensar "como diabos eu sobrevivia antes sem isso?"

Se ainda não conhece, trate de experimentar o excelente Code Climate criado por Bryan Helmkamp, um rubista muito conhecido na comunidade por contribuições em diversos projetos open source, incluindo o próprio Rails e bibliotecas que já foram muito usadas algum tempo atrás como webrat.

O que o Code Climate faz é muito simples: você cadastra seu projeto público ou privado do Github e ele vai começar a processá-lo até surgir com uma nota que vai de zero a quatro. Eu ainda não vi como é um projeto nota Zero mas considerando que já trabalhamos em alguns projetos de resgate que começou em notas como 2 - e já era lamentável - zero significa "mude de profissão, você não serve para isso."

Veja um dos meus projetos de cliente (e todo projeto de cliente tem o mesmo nível):

Nota de todo projeto de cliente no Code Climate

Aliás, quando recebo código dos outros pra melhorar, rodar no Code Climate é a primeira coisa que eu faço para mostrar a profundidade do buraco de uma forma numérica que todo cliente consegue entender.

Mas como ele faz isso? Ele analiza todos os arquivos Ruby do projeto calculando itens de Complexidade (quantidade de assignments (mudança de valores de variáveis), branches (ifs, ifs dentro de ifs)), Complexidade por Método, Duplicação de código, quantidade de vezes que o arquivo foi modificado no Git, quantidade de linhas de código.

Além disso ele faz um scan de segurança, buscando por 20 buracos comuns de segurança como SQL Injection, Cross Site Request Forgery, Denial of Service, Session Setting e assim por diante.

E finalmente, ele se integra com o Travis-CI e checa por cobertura de testes.

Melhor ainda: a cada git push que você dá no seu repositório, o Code Climate automaticamente puxa a atualização e processa tudo de novo e rapidamente envia um email a todos da equipe dizendo se a nota subiu ou caiu, incentivando a consertar o que piorou de qualidade imediatamente. É o mesmo conceito de um continuous integration como o Travis-CI que também executa em paralelo e imediatamente diz se um teste quebrou ou se a cobertura de testes diminuiu.

O correto é sempre estar a 100% de qualidade "conhecida" e 100% de segurança "conhecida" e 100% de cobertura de testes. Começando com tudo a 100%, agora é só manter o nível e consertar imediatamente quando ela cair. Não existe forma melhor de desenvolver. O errado é a equipe trabalhar com um código "opaco", uma caixa preta que ninguém tem certeza se tem algum mínimo de qualidade, onde as pessoas "acham" que o código deve estar bom, onde os testes "provavelmente" rodam e passam. Esse é o jeito mais errado de se desenvolver.

E é errado porque a nota do Code Climate não significa que o código está realmente bom, ele apenas indica o que é tão óbvio que é errado que até um processo automatizado é capaz de encontrar. O Code Climate não é um auditor de código, ele meramente detecta alguns dos muitos "code smells". Um smell pode ser um falso-positivo, ou seja, não há outra maneira mesmo de se desenvolver de outra forma.

Ou seja, ter nota perto de 4 no Code Climate é o mínimo que todo projeto deve ter. Notas 2 e abaixo disso são projetos vergonhosos, dignos de se jogar fora.

Rubistas Sádicos

Por coincidência, a primeira palestra/screencast que eu já fiz foi uma tradução literal de outra excelente palestra. Ela se chama "Machucando Código por Diversão e Lucro" que publiquei em 24 de Junho de 2008.

A palestra original foi apresentado por Ryan Davis, mais conhecido como @zenspider, um dos rubistas mais antigos e não tão conhecido. Ele criou algumas ferramentas muito importantes junto de um dos melhores grupos de Ruby que já existiu, o Seattle.rb - Seattle Ruby Brigade que apresentou ao mundo alguns dos melhores rubistas da primeira geração, incluindo o próprio Ryan Davis, Evan Phoenix (criador do projeto Rubinius), Eric Hodel (mantenedor do Rubygems, RDoc), Geoffrey Grosenbach (do Peepcode), Aaron Petterson (mais conhecido como @tenderlove). Aliás, a Referência Rápida de Ruby do Seattle.rb contém dicas importantes que muitos ainda ignoram.

O que o Bryan fez no Code Climate foi criar uma camada web de usabilidade Software-as-a-Service por cima de algumas bibliotecas bem antigas. Duas delas do Ryan Davis, o Flay e o Flog, duas das ferramentas de tortura essenciais de um Rubista Sádico.

Assista a minha palestra traduzida do Ryan que explica esse assunto em mais detalhes. Mas a nota que o Code Climate gera é baseado nas notas que o Flay e o Flog devolvem ao torturar seu código. Exatamente como descrito no site dos Rubistas Sádicos, o Flog lhe mostra os códigos mais tortuosos que você escreveu, quanto mais doloroso o código, maior a nota. O Flay analiza a estrutura do código Ruby por similaridades estruturais; diferenças de nomes de variáveis, espaços em branco, e estilo de programação são ignorados.

Além do Flay e do Flog, o Bryan adicionou o projeto Brakeman, que é o scanner de segurança. O Bryan explica como ele calcula a métrica do Code Climate em seu site.

Uma coisa que todo bom rubista já fez foi executar essas ferramentas manualmente em seus projetos. O Flay, Flog, Heckle, SimpleCov, Brakeman, RubyProf e outros analisam seu código geram relatórios que lhe dão indicativos de como melhorar cada vez mais seus projetos. Você pode inclusive fazer o mesmo que o Bryan: amarrar a execução dessas ferramentas em alguma runner como o Jenkins, por exemplo. Vale o exercício, quem vai criar o primeiro Code Climate open source?

Seja um Rubista Sádico e machuque seu código por diversão e lucro também. Código não revida, quanto mais você machucá-lo, mais vai se divertir. Código que não é massacrado o tempo todo é muito frágil, e merece ser destruído.

[Dica Rápida] Timeout no Heroku

$
0
0

Ano passado, quando um projeto de cliente entrou em produção, ter esquecido disso me custou várias horas de muita tensão. Graças à ajuda rápida e precisa da equipe de suporte e desenvolvimento do Heroku (thanks @ped) conseguimos contornar a situação.

O Heroku continua sendo minha recomendação a todos os meus clientes. A menos que você tenha uma situação presente muito particular (e lhes garanto, são casos raros), 99% dos casos cabem como uma luva no Heroku. Portanto, na dúvida, vá de Heroku.

Porém, o Heroku tem algumas peculiaridades que você precisa conhecer. Caso ainda não tenha visto, reveja meu artigo Enciclopédia do Heroku que publiquei em Abril de 2012.

Uma em particular merece atenção especial porque a maioria sempre esquece disso. O Router do Heroku evoluiu bastante desde o começo e também desde a controvérsia da RapGenius que estourou no começo de 2013. Mas o artigo não é sobre isso. Alguns já devem ter tentado navegar numa aplicação pesada que colocou no Heroku e receber uma página com um erro genérico roxa do próprio Heroku e não saber o que é. Ou, se você investiu pesado em marketing e começou a receber toneladas de acesso (dezenas ou centenas de requests por minuto), ver seus dynos patinando sem saber porque.

Então aqui vai a dica. Use a gem rack-timeout e configure um timeout baixo, igual ou menor que 15 segundos (que, convenhamos, se uma request demora 15 segundos pra ser processada é porque ela é extremamente mal feita. Culpe seu código antes de culpar o Rails, o Heroku ou qualquer outra coisa).

Para instalar é muito fácil. Adicione à sua Gemfile:

1
gem 'rack-timeout'

Rode bundle pra instalar e crie o arquivo config/initializers/rack_timeout.rb com o seguinte:

123
ifdefined?(Rack::Timeout)Rack::Timeout.timeout = Integer( ENV['RACK_TIMEOUT'] || 12 )end

Finalmente, configure sua aplicação no Heroku com o timeout que você quer, por exemplo:

1
heroku config:set RACK_TIMEOUT=10

E se você usa Unicorn, configure seu config/unicorn.rb com o seguinte:

123456789101112131415161718192021
# Based on https://gist.github.com/1401792

worker_processes 2

timeout 25

preload_app true

before_fork do |server, worker|
  if defined?(ActiveRecord::Base)
    ActiveRecord::Base.connection.disconnect!
    Rails.logger.info('Disconnected from ActiveRecord')
  end
end

after_fork do |server, worker|
  if defined?(ActiveRecord::Base)
    ActiveRecord::Base.establish_connection
    Rails.logger.info('Connected to ActiveRecord')
  end
end

Especial atenção à configuração de timeout. O timeout do Router do Heroku, por padrão, será sempre 30 segundos. Configure o timeout do Unicorn pra ser menor que 30 segundos (como no exemplo, 25 segundos), e configure o Rack Timeout pra ser menor ainda do que isso (no exemplo, 10 segundos).

Leia o README do Rack Timeout para mais detalhes, mas feito isso, se algum processamento levar mais do que 10 segundos para finalizar, ele vai estourar uma exception. E é isso que queremos, caso contrário o Router do Heroku vai cortar após os 30 segundos e não vai lhe dizer onde foi o problema, o gargalo que levou a dar um timeout acima dos 30 segundos.

Agora, você precisa de uma forma de ver o stacktrace gerado e para isso use uma opção simples como Exception Notification que você configura facilmente primeiro adicionando uma opção de envio de email à sua aplicação no Heroku como Sendgrid ou Mailgun, e então adiciona ao seu arquivo config/environments/production.rb algo como:

123456
config.middleware.use ExceptionNotification::Rack,:email => {:email_prefix => "[Exception - MyApp] ",:sender_address => %{"no-reply" <no-reply@myapp.com.br>},:exception_recipients => [ENV['EXCEPTION_NOTIFICATION_EMAIL']]
  }

Ou então adiciona algo mais parrudo como Honeybadger. O importante é você receber esse stacktrace. Com essa informação você pode otimizar sua aplicação. Talvez seja o caso de otimizar uma query muito lenta, talvez faltem índices nas suas tabelas (veja a gem Bullet), talvez seja uma questão de adicionar Caching, talvez você devesse mover um processo demorado como uma tarefa assíncrona via Sidekiq. Enfim, existem diversas opções para melhorar o tempo de uma requisição para que ela fique dentro do que eu considero como "bom" (abaixo de 1 segundo) ou "ótimo" (abaixo de 100ms).

E falando em otimização de requests, outra excelente opção ao Unicorn para colocar no Heroku é o Phusion Passenger que, com o novo garbage collector e a implementação de Out-of-Band Garbage Collector, pode diminuir dramaticamente o tempo das suas requests.

O importante deste artigo: você precisa de informação antes de saber como agir. O timeout default do Router do Heroku não vai lhe dizer, mas o Rack Timeout pode ser o que falta para descobrir seu gargalo, então configure o quanto antes.

Introdução ao Rails Composer

$
0
0

Este é um artigo bem curto para uma dica que é útil tanto para quem está começando como para quem cria muitos projetos o tempo todo. Todos conhecemos o bom e velho comando rails new novo_projeto que vai criar uma estrutura padrão de projetos. Porém, poucos de nós realmente usamos a estrutura padrão, sempre precisamos de muito mais hoje em dia. Em virtude desse requerimento, o Rails suporta um arquivo de templates onde você pode modificar o comportamento do rails new logo em seguida. Mas esse arquivo é um script chato de manter, chato de configurar.

Em vez disso podemos usar uma outra ferramenta que existe há algum tempo chamado Rails Composer. Assumindo que você já tenha RVM ou Rbenv instalado e configurado:

12
gem install rails
gem install rails_apps_composer

Agora, crie um diretório para seu projeto e chame o comando rails_apps_composer:

123
mkdir novo_projeto
cd novo_projeto
rails_apps_composer new . -r core

Ao executar esse comando ele vai começar a criar a nova estrutura de projeto e ao longo do caminho vai lhe fazer diversas perguntas para que você possa customizar seu projeto. Vamos ver as principais perguntas:

1
Would you like to skip Test::Unit? (yes for RSpec) (y/n) y

Certamente, queremos Rspec. Não nade contra a maré, já discuti o assunto Test::Unit vs RSpec anos atrás. Aprenda Rspec de uma vez!

1
Would you like to skip Active Record? (yes for MongoDB) (y/n) n

Essa vai ser mais controversa mas confie em mim: se você tem alguma dúvida sobre NoSQL, não use NoSQL. 99,99% dos casos você vai se dar muito melhor usando um SQL decente (ou seja, Postgresql). Já discuti brevemente esse assunto também.

1
What gem would you like to add? (blank to finish)

Se tiver dúvidas, pode deixar esta em branco. Algumas das coisas que você pensar provavelmente vão aparecer nas próximas perguntas. Mas de cabeça, certamente gems como simple_form, friendly_id, high_voltage, kaminari, rails_12factor, better_errors são alguns bons exemplos de gems a se acrescentar. Mas novamente, dependendo do template que escolher a seguir, muitas dessas gems já virão juntas.

123456789
 question  Build a starter application?
       1)  Build a RailsApps example application
       2)  Build a contributed application
       3)  I want to build my own application
railsapps  Enter your selection: 1

 question  Please upgrade to Rails 4.1 for more starter apps.
       1)  learn-rails
railsapps  Enter your selection: 1

Esta é mais complicada e depende da versão de Rails que escolheu usar. A versão 3.x tem mais templates de aplicativos, a versão 4.1 também, mas a 4.0 só tem a "learn-rails". Veja diretamente na receita do composer para entender. Vou escolher a primeira opção neste exemplo, vale a pena experimentar vários deles pra ver a diferença aqui.

12345678
  question  Web server for development?
      1)  WEBrick (default)
      2)  Thin
      3)  Unicorn
      4)  Puma
      5)  Phusion Passenger (Apache/Nginx)
      6)  Phusion Passenger (Standalone)
   setup  Enter your selection: 2

Aqui é uma questão de preferência. Alguns preferem usar exatamente o mesmo servidor em development que em production. Eu particularmente sempre escolho Thin, mas mais por costume.

12345678
  question  Web server for production?
      1)  Same as development
      2)  Thin
      3)  Unicorn
      4)  Puma
      5)  Phusion Passenger (Apache/Nginx)
      6)  Phusion Passenger (Standalone)
   setup  Enter your selection: 3

Novamente, aqui é mais por costume também que escolho Unicorn. Mas em production o Puma tem se provado uma excelente opção. O Phusion Passenger também está bastante competitivo contra o Unicorn. Vale instalar o New Relic e fazer testes em produção experimentando e tunando essas opções.

12345
  question  Template engine?
      1)  ERB
      2)  Haml
      3)  Slim
   setup  Enter your selection: 3

Esta vai ser controversa, normalmente vai depender da qualidade e motivação dos seus programadores front-end. Na prática, hoje eu prefiro Slim. Por um motivo simples: ele tem quase a mesma performance do ERB e é mais legível e agradável de editar do que ERB. HAML pode ser duas vezes mais lento que ERB ou Slim, então prefiro não usá-lo.

1234
  question  Continuous testing?
      1)  None
      2)  Guard
   setup  Enter your selection: 1

Eu não uso muito o Guard, mas novamente é uma questão de gosto. Normalmente me faz perder mais tempo do que ganhar só na configuração dele, conflitos de coisas que deveriam ter recarregado e não recarregou, specs que passam quando o Guard está rodando mas quando rodo a suite toda dá pau e outros mistérios que desisti de tentar resolver.

12345678
  question  Front-end framework?
      1)  None
      2)  Bootstrap 3.0
      3)  Bootstrap 2.3
      4)  Zurb Foundation 5.0
      5)  Zurb Foundation 4.0
      6)  Simple CSS
   setup  Enter your selection: 2

Novamente, uma questão de gosto. Um projeto para cliente, sério, certamente vai ser CSS do zero. Para coisas pequenas, protótipos, aplicativos pra jogar fora depois, tanto faz Bootstrap ou Zurb. Como aqui é um demo, então vou de Bootstrap 3.

12345
extras  Set a robots.txt file to ban spiders? (y/n) y
extras  Create a GitHub repository? (y/n) n
extras  Use or create a project-specific rvm gemset? (y/n) n
...
extras  Add 'therubyracer' JavaScript runtime (for Linux users without node.js)? (y/n) y

As extras acima são simples. E depois disso bang você tem uma nova aplicação Rails mais completa e pronta para realmente começar a trabalhar do que o esqueleto padrão do Rails que é espartano demais e você iria acrescentar tudo isso manualmente de qualquer jeito.

E, claro, esta foi só a introdução para quem nunca viu esta gem e talvez tenha ficado em dúvida de o que ela faz. Mas além de poder escolher suas opções interativamente você pode criar suas próprias receitas. A documentação do arquivo README vai mostrar muito mais opções. Este artigo apenas mostra o pico do iceberg.

Divirtam-se!

[Heroku Tips] S3 Direct Upload + Carrierwave + Sidekiq

$
0
0

O problema que este artigo vai tentar detalhar é "upload e processamento de imagens". Algo simples como subir uma foto no seu perfil. Assumindo que todos já leram meus artigos sobre o Herokué hora de mais uma dica muito importante.

Arquitetura Mais Comum

Neste exemplo de ilustração vemos o que a maioria das pessoas iniciando implementa:

  1. Seu browser terá um form multipart e um campo file para escolher um arquivo de imagem e fazer o POST para seu controller do lado Rails

  2. O servidor web recebe o arquivo inteiro primeiro (ex. NGINX) e só então passa pra aplicação. Seu controller recebe esse arquivo, instancia o model adequado com um uploader de Carrierwave montado. O uploader inicia o processamento das imagens, digamos, 5 versões de tamanhos diferentes, usando ImageMagick.

  3. Opcionalmente, o uploader faz o upload das novas versões ao AWS S3. Finalmente, retorna ao controller que tudo deu certo e o controller, por sua vez, devolve uma página de sucesso ao browser.

Se estiver rodando num VPS ou Cloud Server como na Digital Ocean, onde você tem acesso à configuração da máquina, provavelmente vai sentir alguma lentidão. Mas não será o fim do mundo. Porém, particularmente se estiver no Heroku as chances são que você vai encontrar o seguinte erro:

Heroku Application Error Page

Já falamos sobre isso e a situação está documentada: o Heroku impõe um limite máximo de 30 segundos do momento em que a request deixa o router até receber uma resposta de volta. Uploads podem fazer o timeout acontecer e seu usuário vai ver o erro estourando na sua frente. Existem 3 pontos de lentidão que queremos consertar:

  1. Tempo de upload do arquivo, do browser até a aplicação.

  2. Tempo de processamento das versões das imagens.

  3. Tempo de upload das versões ao S3.

O objetivo é chegar neste novo cenário:

Arquitetura Recomendada

  1. Novamente, seu browser terá um form multipart e campo file, mas além disso terá javascripts para realizar um POST diretamente ao seu bucket no AWS S3, recebendo de volta a URL no S3.

  2. Agora o browser não gasta mais tempo fazendo o upload do arquivo à sua aplicação Rails, ela vai mandar apenas a string da URL.

  3. O controller vai receber a URL e novamente instanciar o model com seu uploader. Só que em vez de processar as diferentes versões da imagem imediatamente, ele vai somente enviar à uma fila no Redis e já retornar a página de sucesso ao usuário. Do ponto de vista do usuário, a requisição acaba aqui.

  4. Assim que tiver tempo, um worker de Sidekiq vai receber essa tarefa que ficou na fila do Redis e então vai puxar a imagem original do S3, processar as novas versões e realizar o upload de todas de volta ao S3. Esse tempo também o usuário não vai sentir.

Neste exemplo, meramente ilustrativo, no primeiro cenário o usuário sentia toda a espera dos 2 segundos. Neste segundo cenário ele não vai sentir o tempo do upload (a página não recarrega, visualmente terá apenas uma barra de progresso durante o 1 segundo), depois vai sentir a espera de, digamos 200 milissegundos que é o tempo de dar POST da URL e receber a página de sucesso.

O resto do tempo de, digamos, 700 ms, vai ser "invisível" pra ele pois vai rodar em background. Ou seja, do ponto de vista do usuário a melhoria será de uma ordem de grandeza de 10 vezes.

Mais do que isso: será um tempo razoavelmente constante independente do tamanho da imagem que ele tente subir. E isso é importante para não atingirmos o limite de timeout do Heroku quando alguém subir imagens gigantes. Nesse exemplo o tempo de processamento da requisição vai ficar na média de 200 ms independente das imagens que receber.

TL;DR - Too Long, Don't Read

Indo direto ao assunto, vamos primeiro assumir que tudo isso que estou dizendo não é novidade para você então vamos à solução, sem muitas explicações:

  1. Para fazer o upload do browser diretamente para seu bucket no AWS S3 use a gem s3_direct_upload. Ele tem suporte inclusive a upload de múltiplos arquivos (mas não vou tratar disso neste artigo) e providencia diversos hooks para callbacks javascript onde você pode customizar comportamentos. Ele também vai ser responsável por renderizar uma barra de progresso simples para mostrar o upload ao usuário. E Não se esqueça da configuração de CORS no S3.

  2. Do lado do Rails, imagino que todos estejam usando o bom e velho Carrierwave para criar uma classe de uploader junto com o Mini Magick para processar as imagens.

  3. Além disso também assumo que estejam usando a gem Fog para fazer upload diretamente ao S3 ou Rackspace Cloud Files ou Google Storage e não deixando os arquivos localmente no seu filesystem.

  4. Agora, também assumo que não seja novidade que o processamento já esteja sendo feito em background via Sidekiq usando as gems Carrierwave Backgrounder ou Carrierwave Direct. Vamos explicar sua utilidade mas no final a solução não vai precisar de nenhum dos dois (!!) A solução mais simples deste exemplo vai usar um Worker simples de Sidekiq diretamente, sem precisar de gems extras.

Pronto, parece simples - mas não é. A combinação de todas essas gems e a quantidade de opções diferentes que você pode fazer poderiam ser documentadas num livro inteiro sobre o assunto.

Adiantando sua dor de cabeça, os pontos que mais vão tirar seu sono são:

  • Compatibilidade com o maldito Internet Explorer, qualquer versão, todas são imprestáveis. Isso não é dor de cabeça, é garantia de enxaqueca, especialmente para fazer o IE 8 ou 7 funcionarem minimamente dentro do aceitável. Aliás já avisando: a barra de progresso do S3 Direct Upload NÃO funciona no IE 9 pra baixo, nem perca seu tempo.

  • Compatibilidade com navegadores de smartphones. Ainda não sei porque, mas nem toda vez que se tenta fazer upload vai sem problemas.

  • Fazer um model que has_many imagens, conseguir fazer o upload de várias na mesma página sem reload, seja via input file habilitado para múltiplos arquivos ou via ajax escolhendo e subindo um a um. Este ponto particularmente não é tão dor de cabeça mas é trabalhoso para não se confundir ao lidar com nested attributes e, no caso do Rails 4, configurar direito o Strong Parameters.

Neste artigo não vou chegar a lidar com esses problemas, mas fica o aviso e colaborações são bem vindas na seção de comentários.

Outra solução que não testei mas parece promissor é usar o SaaS Transloadit. Parece uma ótima idéia terceirizar esta dor de cabeça. Se alguém testar, não deixe de dizer o que achou nos comentários.

Configurando seu Ambiente

Todo o código-fonte de exemplo está no Github. Então faça o clone dele para ver seu código:

12345
git clone https://github.com/akitaonrails/image_uploader_demo.git
cd image_uploader_demo
rvm use 2.0.0@image_upload_demo --ruby-version --create
bundle install
rake db:migrate

Vamos navegar pelo código usando os commits que estão, mais ou menos, organizados nas seções deste artigo.

Este é um simples exemplo de um site que recebe imagens e mostra numa timeline, um Instagram-like bem tosco e que no final tem esta cara:

Image Uploader Demo

Como ele depende de um dyno worker que não é gratuito, vou deixar ele de pé só por alguns dias. Mas você mesmo pode subir uma versão na sua conta de Heroku. Crie uma nova aplicação com PostgreSQL e Redis To Go, crie um dyno worker e faça deployment (bom e velho git push heroku master).

Configuração no Heroku

Versão 1: Tudo Local

No branch master está a versão final, mas vamos navegar para o primeiro commit relevante:

1
git checkout -b step_1 0bd183593e22ed7481f4553ae17665a3cff77f0f

Esta versão tem de relevante o seguinte:

Gemfile:

1234
...
gem 'carrierwave'
gem 'mini_magick'
...

Para a versão mais simples que poderia funcionar só precisamos do carrierwave e mini_magick. Não esqueça de rodar o comando bundle sempre que atualizar a Gemfile.

app/uploaders/image_uploader.rb:

123456789101112131415
classImageUploader< CarrierWave::Uploader::Base
  include CarrierWave::MiniMagick
  storage :file# Override the directory where uploaded files will be stored.# This is a sensible default for uploaders that are meant to be mounted:defstore_dir"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"end# Create different versions of your uploaded files:
  version :normaldo
    process :resize_to_fit => [300, 300]endend

Isto é um uploader padrão de Carrierwave. Se alguma vez já programou um uploader, este é idêntico a todos que você já viu. Como disse, nesta versão o armazenamento é direto no disco local, por isso storage :file.

app/models/photo.rb:

12345678
classPhoto< ActiveRecord::Base
  belongs_to :user
  validates :user, presence: true
  attr_accessible :image, :user_id
  mount_uploader :image, ImageUploader

  scope :recent, -> { order("created_at desc") }end

Aqui a linha importante é o do mount_uploader que configura o ImageUploader no model. Novamente, nada de novo aqui pra quem já fez isso antes.

app/views/home/index.html.slim:

12345
...
= form_for @photo, :html => {:multipart => true} do |form|
  = form.file_field :image
  = form.submit "Upload"
...

Finalmente, um mero form multipart com um campo de file para escolher sua imagem. Ao dar submit o browser vai fazer o POST para nossa aplicação.

Só com isso, se rodar rails s e for para http://localhost:3000 você já deve ser capaz de subir imagens. Aliás, o esqueleto desta aplicação foi feita com o Rails Apps Composer que mencionei no post anterior. Então ele já tem Devise pré-configurado e você precisa criar uma nova conta antes de conseguir subir uma imagem. O resto do código lida com a associação do model User como Photo. Também adicionei o PureCSS para o site não ficar totalmente feio. O tema é o blog de exemplo deles que copiei e não alterei muita coisa. Aceito contribuição pra alinhar melhor os elementos ;-)

Versão 2: Processamento Assíncrono

1
git checkout -b step_2 fa0974050406abdf14724b7bb36ddd0525fc96b7

Aqui é onde muitos estão aprendendo a chegar ainda. Depois do Rails receber a imagem do form e passar para o ImageUploader, queremos que ele processe a versão :normal e transfira os arquivos para o AWS S3. E queremos que ele não perca tempo da requisição fazendo isso, mas que processe mais tarde, em background.

No modo normal o Carrierwave se coloca no callback de after_commit do ActiveRecord e quando o model salva ele processa as imagens dentro da transaction. Se o processamento der errado ele inclusive faz rollback na transaction se o banco de dados suportar. Ou seja, é bem amarrado, o que é péssimo.

Por isso existem gems como Carrierwave Backgrounder cujo objetivo é deferir o processamento usando gerenciador de filas como Sidekiq, Resque, Delayed Jobs e outros. Para configurá-lo fazemos:

Gemfile:

123456
gem 'carrierwave_backgrounder'
gem 'sidekiq'
gem 'sinatra'
gem 'fog'
gem 'unf'
gem 'dotenv-rails'

Adicionamos o Sidekiq porque é o que gostamos mais para rodar tarefas em background. Adicionamos o Sinatra porque o Sidekiq tem uma interface opcional de monitoramento que, se você quiser, precisa do Sinatra.

Também adicionamos o Fog porque vamos aproveitar para configurar o ImageUploader para mandar nossos arquivos para o AWS S3. E se vamos usar o S3 precisamos configurar coisas como Access Key, Secret Access Key, Bucket e Region e por isso adicionamos o dotenv-rails, sobre o qual já escrevi um post.

app/uploaders/image_uploader.rb:

1234567
classImageUploader< CarrierWave::Uploader::Base
  include ::CarrierWave::Backgrounder::Delay# storage :file
  storage :fog
  ...end

No uploader adicionamos o módulo Delay para habilitar o modo assíncrono e aproveitamos para mudar o storage para o Fog para mandarmos para o S3.

app/models/photo.rb:

123456
classPhoto< ActiveRecord::Base
  ...
  mount_uploader :image, ImageUploader
  process_in_background :image
  ...end

Com o método process_in_background indicamos que queremos que ele mande pro Sidekiq em vez de processar na hora. O Carrierwave Backgrounder tem diversas opções, customizações e comandos que podem ser úteis. Estou apenas mostrando o caminho mais simples então leia a documentação para entender mais.

O arquivo config/initializers/carrierwave.rbé muito longo para copiar no post. Mas basta entender que eu coloquei duas configurações diferentes: uma para ambiente de teste e outra para produção. Na primeira ele continua operando localmente e gravando em arquivos, no segundo tem como configurar para que o Fog seja configurado corretamente para mandar os arquivos para o S3.

config/initializers/carrierwave_backgrounder.rb:

123
CarrierWave::Backgrounder.configure do |c|
  c.backend :sidekiq, queue: :carrierwaveend

Isso é só para dizer ao Backgrounder que queremos o Sidekiq. Lembre de configurar no Procfile para ter a linha worker: bundle exec sidekiq -q carrierwave apontando pra fila ("queue") correta. É a mesma linha de comando que você vai rodar localmente para consumir seu Redis local em desenvolvimento.

Como agora a requisição retorna uma resposta de sucesso tão logo envie ao Sidekiq a tarefa de processar as imagens, significa que provavelmente não vamos ter a imagem do tamanho :normal já pronta. Existem diversos truques que você pode implementar mas o mais comum é adicionar uma coluna booleana como image_processing e na vier checar se ela é nula ou não. O Backgrounder checa por uma coluna com esse nome e se existir ele se encarrega de deixar "true" na hora de criar o model e coloca nil depois de processar as versão. Então, na view app/views/home/index.html.slim onde temos a listagem de fotos, podemos fazer:

123456
...
- if photo.image_processing
  = image_tag "animation_processing.gif"
- else
  = image_tag photo.image.url(:normal)
...

E ele vai mostrar a imagem temporária de "processando". Até aqui é onde a maioria dos iniciantes chega quando está experimentando com upload. E como disse antes, se estiver num VPS ou Cloud Server que consegue controlar, normalmente indo até aqui já é suficiente.

Isso porque uma máquina padrão não tem o limite fechado de menos de 30 segundos. Se alguém se pendurar no seu NGINX ele não vai reclamar por um bom tempo, e você ainda pode aumentar se for o caso. E a aplicação Rails não vai travar por trás. Isso porque o NGINX foi feito pra aguentar esse tipo de pancada. Ele é desenvolvido sobre I/O assíncrono, então se precisar ficar em espera, ele continua atendendo outras requisições até voltar o callback.

No caso de upload ele vai receber o arquivo inteiro antes de mandar pra aplicação Rails atrás. Em muitos casos os sites ou aplicações não são tão pesadas em imagens. Normalmente é um upload de avatar de perfil, ou então um CMS onde somente alguns poucos usuários com permissão podem subir imagens. Então não chega a ser um peso pro servidor. Agora, se sua aplicação for pesada em uploads, algo como clones de Instagram, Flicker, SnapChat, daí você vai precisar continuar vendo as próximas seções.

Mais do que isso, se quiser ir mais longe, ainda pode configurar o módulo de upload do NGINX para suportar uploads parciais. Ou seja, "resume", continuar um uploade de onde parou. Mas, não vou chegar tão longe neste artigo.

Versão 3: S3 Direct Upload

1
git checkout -b step_3 3118bd4dc7faf100fbb97996e009a27c2253b0ff

A partir de agora começa a parte mais "chata". Na verdade o conceito é simples mas como envolve Javascript, compatibilidade de browsers, bugs de Internet Explorer, e outros mistérios de front-end, vou considerar essa a parte mais chata.

O primeiro conceito que você precisa entender é que uma aplicação em um domínio não deveria poder fazer nenhuma ação além de HTTP GET em outro domínio. É o que gera os famigerados problemas de Cross Site. Para aliviar o problema criou-se o conceito de CORS ou Cross Origin Resource Sharing, como o nome diz, compartilhamento de recursos cross origem.

O que queremos é do browser fazer um POST da imagem diretamente ao nosso bucket no AWS S3. Para isso precisamos configurar permissões e política de CORS.

Novamente, assumindo que você sabe pelo menos criar uma conta na AWS e criar buckets no S3, primeiro vamos criar uma permissão para "Everyone":

S3 Management Console

Agora, vamos editar a configuração de CORS com uma política bem flexível:

CORS Configuration

12345678910
<?xml version="1.0" encoding="UTF-8"?><CORSConfigurationxmlns="http://s3.amazonaws.com/doc/2006-03-01/"><CORSRule><AllowedOrigin>*</AllowedOrigin><AllowedMethod>PUT</AllowedMethod><AllowedMethod>POST</AllowedMethod><MaxAgeSeconds>3000</MaxAgeSeconds><AllowedHeader>*</AllowedHeader></CORSRule></CORSConfiguration>

Preste atenção especialmente a "AllowedOrigin" que deve ser o domínio da sua aplicação e "AllowedHeader" que deve fornecer só o mínimo de informações que se precisa. Para nosso exemplo não vou me aprofundar, mas a documentação da própria AWS deve ser suficiente.

Agora queremos mudar nosso form para que ele faça o upload direto pro S3 e para isso vamos usar a gem s3_direct_upload.

Gemfile:

123
...
gem 's3_direct_upload'
...

app/views/home/index.html.slim:

12345678
= s3_uploader_form callback_url: photos_path, post: root_url, as: "photo[image_url]", max_file_size: 5.megabytes, id: "s3-uploader"
  = file_field_tag :file

script id="template-upload" type="text/x-tmpl"
  | <divid="file-{%=o.unique_id%}"class="upload">
  |   {%=o.name%}
  |   <divclass="progress"><divclass="bar"style="width: 0%"></div></div>
  | </div>

Leia a documentação do s3_direct_upload. Aqui estamos substituindo o form antigo por este novo. O s3_uploader_form permite muitas opções. Em particular o callback_urlé a URL na nossa aplicação pra onde ele vai dar POST com a URL do S3 depois que terminar o S3.

O bloco de script é um template. O plugin de JQuery que vai se ligar ao form vai criar um clone desse bloco a anexar no fim do form para controlar o evento de barra de progresso. E falando nisso:

app/assets/javascripts/application.js:

123456
...//= require s3_direct_upload//= require_tree .
jQuery(function() {return$("#s3-uploader").S3Uploader();
});

Como qualquer plugin JQuery que você já deve ter visto, usamos o id do form que configuramos antes na view e configuramos o plugin. O método S3Uploader() vai ligar todos os eventos que precisamos. É assim que ele controla a barra de progresso e depois faz o POST via Ajax pra aplicação, dentre outros eventos. A documentação detalha bem como configurar mais do que o padrão.

app/assets/stylesheets/application.css

123
...*= requires3_direct_upload_progress_bars
...

Não esqueça de adicionar os styles, são eles que vão dar o visual da barra de progresso.

app/controllers/photos_controller.rb:

123456789101112
classPhotosController< ApplicationControllerdefcreate@photo = current_user.photos.buildif params[:url]@photo.remote_image_url = params[:url]else@photo.attributes = params[:photo]end@photo.save
    redirect_to root_pathendend

O controller que vai receber o POST no callback não vai receber um params[:photo] normal, mas um conjunto de informações do arquivo que subiu no S3. Em particular estamos interessados no params[:url] que podemos usar pra anexar no ImageUploader que já temos ou qualquer outro campo do nosso model para processarmos depois.

Na hora que fiz este exemplo não tinha me dado conta disso, mas este trecho tem um grande defeito. O método remote_image_url que na verdade é um método de metaprogramação que usa no nome do seu uploader (ou seja, se seu uploader se chama "avatar" o método vai se chamar "remote_avatar_url"), faz muitas coisas por baixo dos panos em vez de somente gravar a string na tabela.

Ao atribuir a URL nesse método ele vai se conectar ao S3 e baixar o arquivo num diretório temporário. Vamos ver na última seção como consertar isso, mas por enquanto fica o exemplo apenas para ilustrar como do s3_direct_upload sua aplicação Rails pode receber a URL do arquivo que já foi gravado remotamente no S3.

Finalmente, o s3_direct_upload precisa da mesma configuração do Carrierwave/Fog para saber para onde no S3 mandar os arquivos:

config/initializers/s3_direct_upload.rb:

1234567
S3DirectUpload.config do |c|
  c.access_key_id = ENV['AWS_ACCESS_KEY']
  c.secret_access_key = ENV['AWS_SECRET_KEY']
  c.bucket = ENV['AWS_BUCKET']
  c.region = ENV['AWS_REGION']
  c.url = "https://#{ENV['AWS_BUCKET']}.s3.amazonaws.com"end

Usamos a mesma coisa que já configuramos com o dotenv-rails e que você pode configurar em produção com o bom e velho comando heroku config:set.

Versão 4: Manipulando o Form do S3 Direct Upload

1
git checkout -b step_4 72ad3271762882f71e4c9108bc3ff44a8c18da17

Digamos que queremos aumentar a funcionalidade desse site de exemplo. Em vez de somente subir uma imagem, e se quisermos também subir um comentário?

Para começar, basta criarmos a migration para adicionar um campo de comment no modelPhoto, adicionar as as validations. necessárias. Não vou copiar o trecho da migration nem do model porque isso imagino que todos saibam o que fazer, você pode vê-los nos dois links anteriores.

Outro detalhe, até a seção anterior estávamos colocando as coisas no HomeController e no app/views/home. Agora movi para o PhotosController e app/views/photos para poder tratar como resources no routes.rb, vai ficar mais limpo.

Agora, a grande mudanças está em app/views/photos/index.html.slim. Para ficar mais curto, relembre o trecho da view que fizemos na seção anterior, com o s3_uploader_form e o template da barra de progresso. Acima disso vamos colocar um novo form:

123456789101112131415
= form_for @photo, html_options: { class: "pure-form" } do |form|
  - if @photo.errors.any?
    div id="error_explanation"
      h2
        = "#{pluralize(@photo.errors.count, "error")} prohibited this photo from being commented:"
      ul
      - @photo.errors.full_messages.each do |msg|
        li = msg
  fieldset.pure-group
    = form.text_field :comment, class: "pure-input-rounded"
    = form.hidden_field :remote_image_url
    label.pure-button for="file"
      | Choose Image
    = form.submit "Submit Comment", class: "pure-button pure-button-primary"
div.progress_bar

É um form normal de Rails (que, aliás, ficaria muito melhor com Formtastic ou Simple Form, mas o exemplo é tão simples que não vou tão longe hoje). Sendo um form normal, colocamos todos os campos normais da Model que editaríamos, neste exemplo temos o campo :comment.

De importante temos o campo :remote_image_url que é hidden (escondido) porque ele vai ser preenchido depois do upload. E temos um elemento div com a classe .progress_bar. Isso porque o form do s3 direct upload vamos esconder movendo pra fora da tela, e como o plugin dele anexava a barra de progresso nele mesmo, então até a barra ficaria pra fora da tela. Então precisamos de outro div para dizer ao plugin que é onde ele deve passar a anexar a barra de progresso.

Pra esconder o form pro S3 editamos o app/assets/stylesheets/application.css, adicionando:

12345
form#s3-uploader {position: absolute;top: 500px;left: -9999px;
}

Não use display:none ou o plugin vai falhar. Além disso você deve se perguntar: "se escondermos esse form, como vamos selecionar a imagem?" Se olhar o novo form vai notar que ele tem um elemento label for="file". E não tem um elemento file no novo form, então ele "magicamente" vai fazer usar o elemento file do form escondido. E isso serve nosso propósito porque daí os eventos do plugin de file upload vão funcionar como antes.

Agora precisamos dizer ao plugin do s3_direct_upload para anexar o template de barra de progresso no nosso novo div. Pra isso editamos app/assets/javascripts/application.js:

123456789101112131415
jQuery(function() {var s3_uploader = $("#s3-uploader");var uploader = s3_uploader.S3Uploader({progress_bar_target: $(".progress_bar"),
  });

  s3_uploader.bind("s3_uploads_start", function(e, content) {$("#new_photo input[type=submit]").hide();
  });

  s3_uploader.bind("s3_upload_complete", function(e, content) {$("#new_photo input[id=photo_remote_image_url]").val(content.url);$("#new_photo input[type=submit]").show();
  });
});

Com calma agora. O initializer do plugin aceita um hash de opções e um deles é o progress_bar_target que recebe um elemento JQuery. Agora a barra de progresso vai ser anexada nele. Em seguida temos os eventos de s3_uploads_start que é logo que inicia o upload e o s3_upload_complete que é logo que o upload termina. Só de perfumaria fiz o botão de submeter o novo form ficar escondido enquanto está fazendo upload e no final mostra de volta o botão e colocar a URL do arquivo, que vem em content.url, no campo escondido que mencionei antes e cujo id, nesse caso, é photo_remote_image_url. Novamente, veja a documentação completa, existem outros eventos e outras configurações que você pode mexer.

O resto é praticamente a mesma coisa. Não esquecer de editar o model em app/models/photo.rb para ter attr_accessible :image, :user_id, :remote_image_url, :comment, ou no caso do Rails 4 não deixe de configurar o params.permit(). Vai ser um mass assignment de parâmetros e a URL, neste exemplo, vai estar em params[:photo][:remote_image_url].

A partir daí, depois que o controller passar os parâmetros pro model, ao salvar os callbacks do Carrierwave Backgrounder vão mandar o processamento pro Sidekiq, como antes. Mas agora você tem flexibilidade no formulário no front-end e pode customizar como achar melhor.

Versão 5: Active Admin

1
git checkout -b step_5 3a07f7d6ef86ea8db6a191353676149460200777

Tirando o engasgo que mencionei do campo remote_image_url, tudo parece funcionar perfeitamente agora. Você já manda as imagens diretamente pro S3, no Rails o processamento pesado vai pro Sidekiq. O que falta?

Normalmente uma boa aplicação tem algum tipo de Administração. Para gerenciar usuários, apagar elementos impróprios e assim por diante. E por experiência digo que, na dúvida, coloque o bom e velho ActiveAdmin. Ele é simples e mesmo se precisar de alguma customização ele não é complicado.

Vou economizar espaço e não vou mostrar como se configura o ActiveAdmin. Veja este commit para saber como.

O que vamos fazer é a mesma coisa que antes:

  1. Adicionar o s3_uploader_form escondido embaixo do form. Para esconder colocamos o mesmo CSS de antes em app/assets/stylesheets/active_admin.css.scss.

  2. Adicionar os javascripts do plugin e dos eventos que precisamos customizar. É praticamente o mesmo de antes e colocamos em app/assets/javascripts/active_admin.js.

  3. Criar um campo hidden no form original. Para customizar forms no activeadmin que a DSL não é suficiente podemos fazer o seguinte:

Em app/admin/photos.rb colocamos form :partial => "form" e com isso dizemos a ele para procurar uma partial normal.

E a partial fica em app/views/admin/photos/_form.html.slim e usamos o mesmo Formtastic que ele usa, ficando assim:

1234567891011121314151617
= semantic_form_for [:admin, @photo] do |f|
  = f.inputs do
    = f.input :user
    = f.input :comment
    = f.input :remote_image_url
    label.pure-button.pure-button-active for="file"
      | Choose Image
  = f.actions

= s3_uploader_form as: "photo[image_url]", id: "s3-uploader", class: "pure-form pure-form-stacked"
  = file_field_tag :file

script id="template-upload" type="text/x-tmpl"
  | <divid="file-{%=o.unique_id%}"class="upload">
  |   {%=o.name%}
  |   <divclass="progress"><divclass="bar"style="width: 0%"></div></div>
  | </div>

Veja que é praticamente a mesma coisa. O s3_uploader_form vai ficar escondido. No Javascript falamos para ele anexar a barra de progresso no form do Formtastic fazendo progress_bar_target: $(".formtastic.photo"). E no form não esquecer do elemento label for="file" que é o trigger que vai abrir o selecionador de arquivos no form que está escondido.

Duas dicas genéricas de ActiveAdmin é que ele pode reclamar da ausência do "jquery-ui". Então adicione a gem "jquery-ui-rails" na Gemfile e crie os seguintes arquivos:

app/assets/javascripts/active_admin.js:

1
//= require active_admin/base

app/assets/javascripts/jquery-ui.js

1
//= requirejquery.ui.all

E a outra dica é um problema com rotas, e para garantir que tudo funciona no config/routes.rb coloque a linha ActiveAdmin.routes(self) no final do arquivo ou pelo menos depois da configuração do Devise.

O resto do código é para mostrar a imagem na listagem do ActiveAdmin e para mostrar a imagem de "processando" caso as versões ainda não tenham sido processadas no Sidekiq.

Versão 6 (FINAL!)

1
git checkout -b step_6 c112d2eb7023932067f05acd8a5bacd6ed6c3d28

Finalmente, vamos consertar o que disse antes sobre o campo remote_image_url. Recapitulando, depois que o S3 Direct Upload termina o upload ao S3, fazemos o Javascript gravar a URL no campo remote_image_url, que é criado no model pelo Carrierwave. E via mass assignment, o controller manda pra dentro do model. Só que quando esse campo é configurado, o Carrierwave faz o download do arquivo. E downloads demoram, eliminando as vantagens que queríamos de fazer o Rails ser o mais rápido possível.

A "correção" é razoavelmente simples. Vamos criar um novo campo no model Photo. Esta é a migration:

12345
classAddRemoteUrlFieldToPhoto< ActiveRecord::Migrationdefchange
    add_column :photos, :original_image_url, :stringendend

Agora substituímos em todos os arquivos, todos os lugares que usamos "remote_image_url" para "original_image_url", que é meramente um campo string.

Além disso, vamos remover o Carriewave Backgrounder. Retire da Gemfile. Dessa forma o Carrierwave volta à forma original e vai processar as versões tão logo o model seja salvo.

O controller app/controllers/photos_controller.rb vai ficar o mais simples possível, como qualquer controller que você já viu por aí:

12345678910111213141516171819202122
classPhotosController< ApplicationController
  before_filter :load_photos_pagedefindex@photo = current_user.photos.build if current_user.present?enddefcreate@photo = current_user.photos.build(params[:photo])if@photo.save
      redirect_to root_urlelse
      render :indexendend

  private

  defload_photos_page@photos = Photo.recent.page params[:page]endend

Isso vai garantir que o controller dinâmico do ActiveAdmin também funcione corretamente. O model em app/models/photo.rb vai ganhar um pouco mais de complexidade:

123456789101112131415161718192021222324
classPhoto< ActiveRecord::Base
  belongs_to :user
  validates :user, presence: true
  validates :original_image_url, presence: true
  validates :comment, length: { maximum: 140 }

  attr_accessible :image, :user_id, :original_image_url, :comment
  mount_uploader :image, ImageUploader

  before_save :check_url
  after_save :process_async

  scope :recent, -> { order("created_at desc") }

  private

  defcheck_urlself.image_processing = trueif new_record? && original_image_urlenddefprocess_asyncProcessImageWorker.perform_async(self.id, original_image_url) if original_image_url && !image_processingendend

Em particular note os eventos em before_save e after_save. No primeiro caso, como o uploader está vazio já que estamos gravando a URL no campo original_image_url em vez do remote_image_url, o Carrierwave não vai processar nada quando o model salvar. Só mudamos o campo image_processing para ser "true" em vez de "nil" para que possamos mostrar a imagem de "processing" nas views. Esse campo image_processing também é checado antes de enviar um job ao Sidekiq porque no job haverá um #save, que por sua vez vai chamar esse callback de novo e isso vai gerar infinitos jobs de Sidekiq. Então garantimos que ele só será chamado uma vez, daí no job o image_processingé zerado e o callback não vai criar outro.

Logo depois que salvar, enfileiramos uma nova tarefa assíncrona de Sidekiq no método process_async. Agora precisamos criar esse worker em app/workers/process_image_worker.rb:

1234567891011
classProcessImageWorker
 include Sidekiq::Workerdefperform(photo_id, url)Photo.find(photo_id).tap do |photo| # 1
      photo.remote_image_url = url      # 2
      photo.image_processing = nil# 3
      photo.save!                       # 4endendend

Vejamos o que ele está fazendo:

  1. Carrega o model a partir do ID que passamos
  2. Agora ele passa a URL para o campo dinâmico remote_image_url. Isso vai fazer o Carrierwave puxar o arquivo do S3 e, quando salvarmos o model novamente, vai processar as versões
  3. Mudamos o campo image_processing para nil. Pra sermos mais precisos, deveríamos fazer isso só depois de chamarmos save, porque o Carrierwave ainda não gerou as versões. Lembre-se disso, mas deixemos assim no exemplo só para simplificar.
  4. Agora chamamos save. E como tiramos o Backgrounder, o Carrierwave vai imediatamente usar o Mini Magick pra gerar as versões e vai subir os novos arquivos no S3 via Fog.

Na prática simplificamos e substituímos o que o Backgrounder faz mas com mais controle sobre o que estamos fazendo.

Conclusão

Arquitetura Recomendada

Finalmente, temos uma aplicação de acordo com a arquitetura que definimos no começo do artigo:

  1. O usuário não sente o upload do arquivo porque ele é feito diretamente ao S3 via Ajax no browser. A nossa aplicação não é tocada enquanto isso.

  2. Depois que o upload termina, via Javascript gravamos a URL do S3 no form e quando fazemos submit ele é rápido porque não vai arquivo nenhum.

  3. No controller recebemos a URL e criamos o model. Como não gravamos a URL nos campos dinâmicos do Carrierwave, ele não vai processar nada. Mas por causa do after_save podemos chamar o Sidekiq e enfileirar uma tarefa pra rodar depois. A aplicação vai devolver HTTP 200 pro usuário e não haverá nenhuma espera.

  4. O usuário vai ver uma imagem de "processando" enquanto o Sidekiq não rodar. Uma melhoria é fazer um Javascript que fica checando a URL da versão. Se voltar HTTP 200 daí ele puxa a imagem e substitui a imagem de "processando" pela real. É algo simples e que vai melhorar a usabilidade.

  5. Quando o Sidekiq finalmente rodar em background, ele vai pegar a URL que foi originalmente pro S3, mandar pro Carrierwave e aí sim, ele vai gastar seu tempo processando tudo e subindo os arquivos pro S3.

Existem várias coisas que não fizemos neste artigo. Não sei se vou fazer um novo artigo então fica de lição de casa ou para quem quiser fazer um artigo para complementar este :-)

  • Permitir selecionar múltiplos arquivos no form S3 Direct Upload. A documentação dele dá exemplos de como fazer isso. Vai precisar de algum Javascript na hora de transferir as URLs para o form normal. E no model vai precisar aceitar nested attributes. Mas feito isso não é para ser muito complicado.

  • Não testei extensivamente em navegadores toscos (IE 6, 7, 8), mas é certeza que a barra de progresso não funciona. Precisa fazer algum tipo de fallback para dar o feedback para o usuário sobre o upload em andamento. Também não fiz testes em smartphones ou tablets. "Teoricamente" deveria funcionar mas ainda não tenho certeza.

  • Não esqueça de limitar a configuração de CORS do seu bucket, caso contrário qualquer um pode subir o que quiser. Quando você for ver seu bucket vai estar cheio de lixo. Se estiver numa VPS ou Cloud Server, use NGINX como proxy para o S3 de forma a controlar melhor o que pode ir para lá.

  • Este aplicativo é um exemplo bem primário, não usa nenhuma das boas práticas que já mencionei em outros artigos, como transferir seus assets também para o S3, por exemplo. Não use ele como aplicativo de modelo para novos projetos. Se alguém quiser melhorar o aplicativo, ele está no Github. Já a aplicação rodando no Heroku pode sair do ar a qualquer momento.

Como eu disse no começo do artigo, este assunto parece muito simples. "O que pode dar errado num mero upload de uma foto?" Muita coisa!

Este artigo foi o resultado de um projeto real, e muito suor e muitas lágrimas. Agradecimentos ao Rafael Macedo que também sofreu comigo até chegarmos na melhor solução desse enrosco. Lembrando que o que narrei neste artigo é uma versão simplificada do que tivemos que fazer. Espero que parte da dor tenha sido transmitida e todos levem esse assunto a sério.

Viewing all 481 articles
Browse latest View live