Init.js: um guia para o porquê e como o JavaScript de pilha completa
Publicados: 2022-03-11A história
Então, você e seu cofundador têm essa ótima ideia para um negócio, certo?
Você vem adicionando recursos em sua mente.
Frequentemente, você pede a opinião de clientes em potencial e todos adoram.
Ok, então as pessoas querem. Há até algum dinheiro a ser feito. E a única razão pela qual eles não podem tê-lo é porque você ainda não o implementou.
Então, finalmente, você se senta um dia e diz: “Vamos fazer isso!” Em breve, você está tentando descobrir como implementar a lógica de negócios do seu aplicativo, o recurso matador que impulsionará o produto: você tem uma ideia de como fazê-lo e sabe que pode fazê-lo.
"Feito! Funciona!" você diz. Sua prova de conceito é um sucesso! Tudo o que resta é empacotá-lo em um aplicativo da web.
“Ok, vamos criar o site”, você diz.
E então, você percebe a verdade: você precisa escolher uma linguagem de programação; você precisa escolher uma plataforma (moderna); você precisa escolher alguns frameworks (modernos); você precisa configurar (e comprar) armazenamento, bancos de dados e provedores de hospedagem; você precisa de uma interface de administração; você precisa de um sistema de permissões; você precisa de um gerenciador de conteúdo.
Você tem dezenas e dezenas de decisões arquitetônicas para tomar. E você quer fazer os corretos: você quer usar tecnologias que permitem desenvolvimento rápido, iteração constante, eficiência máxima, velocidade, robustez e muito mais. Você quer ser enxuto, você quer ser ágil. Você deseja usar tecnologias que o ajudarão a ter sucesso a curto e longo prazo. E nem sempre são fáceis de escolher.
"Estou sobrecarregado", você diz, enquanto se sente sobrecarregado. Sua energia não é a mesma de antes. Você tenta juntar as coisas, mas dá muito trabalho.
Sua prova de conceito lentamente murcha e morre.
A proposta
Depois de abandonar toneladas de ideias dessa maneira, decidi projetar uma solução. Eu o chamo de projeto 'Init' (ou, init.js).
O cerne da ideia é ter um único projeto para iniciar todos eles, para permitir que o desenvolvedor ou o fundador técnico tome todas essas decisões essenciais de uma só vez e receba um modelo inicial apropriado com base nessas decisões. Eu sei o que os detratores vão dizer: “Uma solução não pode ser aplicada a todos os problemas” (odiadores vão odiar). E eles podem estar certos. Mas podemos fazer o nosso melhor para criar uma solução aproximada, e acho que o Init chega bem perto.
Para melhor atingir este objetivo, temos que manter algumas ideias-chave em mente. Ao desenvolver o Init, considerei:
Componentes
Componentização é uma característica chave de qualquer sistema, pois permite reutilizar componentes de software em diferentes projetos – que é o principal objetivo do Init. Mas a componentização também vem com um subproduto, a “substituibilidade”, que será nosso melhor aliado para atacar vários problemas diferentes com “quase” a mesma solução.
Facilidade de Desenvolvimento
Algum problema, em algum lugar tem uma solução melhor escrita em Brainf*ck. Mas implementar essa solução (em Brainfuck) será quase impossível de escrever, muito menos ler. Vai custar-lhe tempo e uma quantidade enorme de esforço. Em geral, você deve usar linguagens e plataformas que tornem o desenvolvimento mais fácil, não mais difícil para você (ou para qualquer pessoa que possa trabalhar nisso mais tarde).
Comunidade
Seja qual for a plataforma escolhida, certifique-se de que ela tenha uma grande comunidade e que possa ajudá-lo com os problemas mais comuns e incomuns. Lembre-se: jQuery pode não ser a biblioteca mais rápida, limpa ou elegante, mas é uma vencedora apenas por causa de sua comunidade.
Mantendo esses objetivos em mente, a seguir mostrarei como tomei minhas próprias decisões ao criar o Init.
Em sua essência, o Init tira vantagem do paradigma 'full-stack JavaScript' (algumas pessoas se referem a ele, ou a um subconjunto dele, como MEAN Stack). Ao trabalhar com essa pilha, o Init é capaz de usar apenas um único idioma enquanto cria um ambiente incrivelmente flexível e cheio de recursos para o desenvolvimento de aplicativos da web. Resumindo, o Init permite que você use JavaScript não apenas para desenvolvimento de cliente e servidor, mas também para construção, teste, modelagem e muito mais.
Mas vamos desacelerar por um momento e nos perguntar: é realmente uma boa ideia usar JavaScript?
Por que eu escolhi JavaScript
Sou desenvolvedor web desde 1998. Naquela época, usávamos Perl para a maior parte de nosso desenvolvimento no lado do servidor, mas desde então tínhamos JavaScript no lado do cliente. As tecnologias de servidor Web mudaram imensamente desde então: passamos por ondas e mais ondas de linguagens e tecnologias como PHP, AP, JSP, .NET, Ruby, Python, só para citar algumas. Os desenvolvedores começaram a perceber que usar duas linguagens diferentes para os ambientes cliente e servidor estava complicando as coisas. As tentativas iniciais de unificação em uma única linguagem tentaram criar componentes cliente no servidor e compilá-los para JavaScript. Isso não funcionou como esperado e a maioria desses projetos falhou (por exemplo: ASP MVC substituindo formulários da Web ASP.NET e GWT provavelmente sendo substituído em um futuro próximo pelo Polymer). Mas foi uma ótima ideia, em essência: uma linguagem única no cliente e no servidor, permitindo reutilizar componentes e recursos (esta é a palavra-chave: resources ).
A resposta foi simples: coloque JavaScript no servidor.
Na verdade, o JavaScript nasceu com o JavaScript Server Side no Netscape Enterprise Server, mas a linguagem simplesmente não estava pronta na época. Após anos de tentativa e erro, finalmente surgiu o Node.js, que não apenas colocou JavaScript no servidor, mas também promoveu a ideia de programação sem bloqueios, mudando a maneira como escrevemos um “fread” (I/O) para sempre (leia aqui para mais).
Mas essas ideias não eram novas, então por que elas se tornaram tão populares com o Node.js? A programação simples e sem bloqueios pode ser obtida de várias maneiras. Talvez o mais fácil seja usar retornos de chamada e um loop de eventos. Na maioria das linguagens, isso não é uma tarefa fácil: enquanto 'callbacks' é um recurso comum em algumas outras linguagens, um loop de eventos não é, e muitas vezes você se vê lutando com bibliotecas externas (por exemplo: Python, com Tornado). Mas em JavaScript, os retornos de chamada são embutidos na linguagem, assim como o loop de eventos, e quase todos os programadores que se interessaram em JavaScript estão familiarizados com eles (ou pelo menos os usaram, mesmo que não entendam bem o que o evento laço é). De repente, todas as startups na Terra poderiam reutilizar desenvolvedores (ou seja, recursos) tanto no lado do cliente quanto no lado do servidor, resolvendo o problema do post de trabalho “Python Guru Needed”.
Portanto, agora temos uma plataforma incrivelmente rápida (graças à programação sem bloqueios) com uma linguagem de programação incrivelmente fácil de usar (graças ao JavaScript). Mas é o suficiente? Será que vai durar? Tenho certeza que o JavaScript terá um lugar importante no futuro. Deixa-me dizer-te porquê:
Programação Funcional
JavaScript foi a primeira linguagem de programação a trazer o paradigma funcional para as massas (é claro, Lisp veio primeiro, mas a maioria dos programadores nunca construiu um aplicativo pronto para produção usando Lisp). Lisp e Self, principais influências do Javascript, estão repletos de ideias inovadoras. Essas ideias podem liberar nossas mentes para explorar novas técnicas, padrões e paradigmas. E todos eles são transferidos para JavaScript. Dê uma olhada em mônadas, números de Igreja, ou mesmo (para um exemplo mais prático) funções de coleções do Underscore.js, que podem economizar linhas e linhas de código.
Objetos dinâmicos e herança prototípica
A programação orientada a objetos sem classes (e sem hierarquias infinitas de classes) permite um desenvolvimento rápido (criar objetos, adicionar métodos e usá-los), mas, mais importante, reduz o tempo de refatoração durante as tarefas de manutenção, permitindo que o programador modifique instâncias de objetos. de aulas. Essa velocidade e flexibilidade abrem caminho para um desenvolvimento rápido.
JavaScript é a Internet
JavaScript foi projetado para a Internet, está aqui desde o início e não vai desaparecer. Todas as tentativas de destruí-lo falharam: veja, por exemplo, a queda dos Java Applets, a substituição do VBScript pelo TypeScript da Microsoft (que compila para JavaScript) e o desaparecimento do Flash nas mãos do mercado móvel e do HTML5. É impossível substituir o Javascript sem quebrar milhões de páginas da web, então nossos objetivos daqui para frente devem ser melhorá-lo. E não há ninguém melhor para o trabalho do que o Comitê Técnico 39 da ECMA.
Ok, alternativas ao JavaScript nascem todos os dias, como CoffeeScript, TypeScript e as milhões de linguagens que compilam para JavaScript. Essas alternativas podem ser úteis para estágios de desenvolvimento (através de mapas de origem), mas falharão em suplantar o JavaScript a longo prazo por dois motivos: suas comunidades nunca serão maiores e seus melhores recursos serão adotados pelo script ECMA (ou seja, JavaScript ). JavaScript não é uma linguagem assembly: é uma linguagem de programação de alto nível com código-fonte que você pode entender, então você deve entendê-lo.
JavaScript de ponta a ponta: Node.js e MongoDB
Então, essas são as razões para usar JavaScript. Agora, usarei JavaScript como motivo para usar Node.js e MongoDB.
Node.js
O Node.js é uma plataforma para criar aplicativos de rede rápidos e escaláveis — é basicamente isso que diz o site Node.js. Mas o Node.js é mais do que isso: é o ambiente de tempo de execução preferido para qualquer aplicativo JavaScript com acesso de E/S. Mesmo que você não planeje escrever seu aplicativo de servidor principal com Node.js, você pode usar ferramentas criadas com base no Node.js para melhorar seu processo de desenvolvimento. Por exemplo: Mocha.js para teste de unidade, Grunt.js para tarefas de compilação automatizadas ou mesmo Brackets para edição de código de texto completo.
Então, se você vai escrever aplicativos JavaScript para o servidor ou cliente, você deve dar uma olhada em alguns exemplos do Node.js, porque você vai precisar e usá-lo diariamente. Existem algumas alternativas interessantes, mas nenhuma delas chega a 10% da comunidade Node.js.
MongoDB
MongoDB é um banco de dados baseado em documentos NoSQL que usa JavaScript como sua linguagem de consulta, permitindo que eu complete a plataforma JavaScript de ponta a ponta. Mas esse nem é o principal motivo para escolher esse banco de dados.
O MongoDB é um banco de dados sem esquema que permite que você persista seus objetos de maneira flexível e, assim, se adapte mais rapidamente às mudanças nos requisitos. Além disso, é altamente escalável e baseado em redução de mapa, o que o torna adequado para aplicativos de big data. O MongoDB é tão flexível que pode ser usado como um banco de dados de documentos sem esquema, um armazenamento de dados relacional (embora não tenha transações) ou até mesmo como um armazenamento de valor-chave para armazenar respostas em cache.
Componentização do servidor com Express.js
A componentização do lado do servidor nunca é fácil. Mas com Express.js (e Connect.js) veio a ideia de 'middleware'. Na minha opinião, middleware é a melhor forma de definir componentes no servidor. Se você quiser compará-lo com um padrão conhecido, é bem parecido com pipes e filtros.
A ideia básica é que seu componente faça parte de um pipeline. O pipeline processa uma solicitação (entrada) e gera uma resposta (saída), mas seu componente não é responsável por toda a resposta. Em vez disso, ele apenas modifica o que precisa e depois delega para a próxima parte do pipeline. Quando a última parte do pipeline termina o processamento, a resposta é enviada de volta ao cliente.
Referimo-nos a esses 'pedaços do pipeline' como 'middleware'. Claramente, podemos criar dois tipos de middleware:
Intermediários : Aqueles que processam a solicitação e a resposta, mas não são totalmente responsáveis pela resposta em si, então eles delegam para o próximo middleware.
Finais : Aqueles com total responsabilidade sobre a resposta final. Eles processam e modificam a solicitação e a resposta, mas não precisam delegar para o próximo middleware. Na prática, é recomendável delegar para um próximo middleware de qualquer maneira para permitir flexibilidade arquitetural (ou seja, adicionar mais middleware posteriormente), mesmo que esse middleware não exista (nesse caso, a resposta irá direto para o cliente).
Como exemplo concreto, considere um componente 'gerenciador de usuários' no servidor. Em termos de middleware, teríamos finais e intermediários. Para nossas finais, teríamos recursos como criar um usuário e listar usuários. Mas antes que possamos realizar essas ações, precisamos de nossos intermediários para autenticação (já que não queremos solicitações não autenticadas chegando e criando usuários). Uma vez que criamos esses intermediários de autenticação, podemos simplesmente conectá-los em qualquer lugar que queiramos transformar um recurso não autenticado anteriormente em um recurso autenticado.
Aplicativos de página única
O projeto Init se concentra na criação de aplicativos de página única (SPAs). A maioria dos desenvolvedores da Web foi tentada mais de uma vez a experimentar os SPAs. Eu construí vários (principalmente proprietários), e posso dizer com confiança que eles são simplesmente o futuro dos aplicativos da web. Você já comparou um SPA a um aplicativo da web comum em uma conexão móvel? A diferença na capacidade de resposta é da ordem de dezenas de segundos.
Os SPAs são o futuro da web, então por que você construiria seu produto em um formato legado? Um argumento comum que ouço é que as pessoas estão preocupadas com SEO. Mas se você lidar com as coisas corretamente, isso não deve ser um problema: o próprio Google tem um tutorial muito bom sobre como fazer isso, e há alguns bons comentários aqui também.
Client Side MV* com Backbone.js, Marionette.js e Twitter Bootstrap
Muito se tem falado sobre frameworks MVC* para SPAs. É uma escolha difícil, mas eu diria que os três primeiros são Backbone.js, Ember.js e Angular.js.
Os três são muito bem vistos. Mas qual é o melhor para você?
Infelizmente, devo admitir que tenho uma experiência muito limitada com Angular.js, então vou deixá-lo fora desta discussão (para saber mais sobre isso, consulte o tutorial Angular.js). Agora, Ember.js e Backbone.js representam duas maneiras diferentes de atacar o mesmo problema.
O Backbone.js é mínimo, simplista e oferece apenas o suficiente para criar um SPA simples. Ember.js, por outro lado, é uma estrutura completa e profissional para a criação de SPAs. Tem mais sinos e assobios, mas também uma curva de aprendizado maior.
Dependendo do tamanho do seu aplicativo, a decisão pode ser tão fácil quanto olhar para a relação featuresUsed/featuresAvailable, que lhe dará uma grande dica.
No caso do Init, eu queria cobrir a maioria dos cenários, então escolhi Backbone.js para facilitar a criação de SPA, com Backbone.Marionette.View para componentização. Dessa forma, cada componente é um aplicativo simples, e o aplicativo final pode ser tão complexo quanto quisermos.
O estilo também é um desafio, mas podemos contar novamente com estruturas para nos salvar. Para CSS, não há nada melhor que o Twitter Bootstrap, que oferece um conjunto completo de estilos prontos para uso e fáceis de personalizar.
O Bootstrap foi criado usando a linguagem LESS, e é de código aberto, então podemos modificá-lo se necessário. Ele vem com uma tonelada de controles de UX que estão bem documentados no site do Bootstrap. Além disso, há um modelo de personalização que permite que você crie o seu próprio. É definitivamente o homem para o trabalho.
Práticas recomendadas: Grunt.js, Mocha.js, Chai.js, RequireJS e CoverJS
Finalmente, devemos definir algumas de nossas melhores práticas e ver como o Init pode ajudá-lo a implementá-las e mantê-las. Nossa solução está centrada em diversas ferramentas, que são baseadas no próprio Node.js.
Mocha.js e Chai.js :
Essas ferramentas permitem que você melhore seu processo de desenvolvimento aplicando TDD ou BDD, fornecendo a infraestrutura para organizar seus testes unitários e um runner para executá-los automaticamente.
Existem milhares de estruturas de teste de unidade para JavaScript. Então, por que usar Mocha.js? A resposta curta: é flexível e completo.
A resposta longa: tem duas características importantes (interfaces, repórteres) e uma ausência significativa (asserções). Deixe-me explicar.
Interfaces : talvez você esteja acostumado a conceitos de TDD de suítes e testes de unidade, ou talvez você prefira ideias de BDD de especificações de comportamento com “describe” e “it should”. Mocha.js permite que você use ambas as abordagens.
Repórteres : a execução de seu teste gerará relatórios dos resultados e você poderá formatar esses resultados usando vários repórteres. Por exemplo, se você precisar alimentar um servidor de Integração Contínua, poderá encontrar um repórter para fazer exatamente isso.
Falta de uma biblioteca de asserções : longe de ser um problema, o Mocha.js foi projetado para permitir que você use a biblioteca de asserções de sua escolha, dando a você ainda mais flexibilidade. Existem muitas opções, mas é aqui que o Chai.js entra em ação.
Chai.js é uma biblioteca de asserções flexível que permite usar qualquer um dos três principais estilos de asserção:
Assert : Estilo de afirmação clássico da velha escola TDD. Por exemplo:
assert.equal(variable, ”value”);
Expect : Estilo de asserção encadeado, mais comumente usado em BDD. Por exemplo:
expect(variable).to.equal(“value”);
Should : Também usado em BDD, mas eu prefiro Expect porque Should soa repetitivo com a especificação de comportamento 'it (“should do something..”)'. Por exemplo:
variable.should.equal(“value”);
Chai.js combina perfeitamente com Mocha.js. Usando apenas essas duas bibliotecas, você pode escrever seus testes em TDD, BDD ou qualquer estilo imaginável.
Grunt.js :
O Grunt.js permite automatizar tarefas de compilação, desde simples copiar e colar e concatenação de arquivos, até pré-compilação de modelos, compilação de linguagem de estilo (ou seja, SASS e LESS), teste de unidade (com mocha.js), linting e minificação de código (por exemplo, com UglifyJS ou Closure Compiler). Você pode adicionar sua própria tarefa automatizada ao Grunt ou pesquisar no registro do Grunt, onde existem centenas e centenas de plugins disponíveis (novamente, vale a pena usar ferramentas com grandes comunidades por trás delas). O Grunt também pode monitorar seus arquivos e acionar ações quando eles são modificados.
Requer JS :
O RequireJS pode parecer apenas mais uma maneira de carregar módulos com AMD, mas posso garantir que é muito mais do que isso. Para entender o porquê, primeiro precisamos mencionar a ideia do namespace do módulo (por exemplo, demo.views.hello), que evita poluir o namespace global envolvendo cada módulo em seu próprio namespace. O problema é que esses módulos não são reutilizáveis: se você modificar o namespace de uma 'instância', estará modificando o namespace de todas as 'instâncias'. Em contraste com isso, o RequireJS permite definir módulos reutilizáveis desde o início. (Além disso, ajudará você a adotar a injeção de dependência para evitar que seus módulos acessem variáveis globais.)
CoverJS :
A cobertura de código é uma métrica para avaliar seus testes. Como o nome indica, ele informa quanto do seu código é coberto pelo seu conjunto de testes atual. CoverJS mede a cobertura de código de seus testes instrumentando instruções (em vez de linhas de código como JSCoverage) em seu código e gerando uma versão instrumentada de seu código. Ele também pode gerar relatórios para alimentar seu Continuous Integration Server.
Usando ramificações para alternar recursos
Quando eu iniciei o Init, eu precisava de uma maneira para os usuários ativarem e desativarem vários recursos que eles poderiam querer em seus projetos. Decidi adotar uma abordagem radical ao sistema de ramificação do git para implementar essa funcionalidade.
Em essência, cada branch representa um recurso ou funcionalidade que um usuário pode querer incluir. Se você estiver iniciando um projeto do zero, comece na ramificação mínima necessária e, em seguida, adicione outras tecnologias mesclando com as ramificações desejadas. Por exemplo, digamos que você deseja iniciar seu projeto com Backbone.js e Marionette.js. Bem, você pode começar na ramificação Backbone.js e mesclá-la com a ramificação Marionette, continuando para cada parte da funcionalidade que você deseja adicionar.
Por enquanto, essa ideia de mesclar para adicionar funcionalidade só pode ser usada para modelos de tecnologia (por exemplo, Backbone, Node, Express). Mas, no futuro, você poderá alternar entre implementações de back-end (por exemplo, do MongoDB para o Postgres) e do cliente.
Inicie um projeto com o Init e implante no Heroku hoje
Nunca houve uma maneira mais fácil de iniciar um projeto. Basta ir ao repositório do GitHub, verificar o branch com os commits mais recentes (agora é usermanager, embora isso possa mudar no futuro) e depois:
- Crie o diretório para seu projeto (ou use um existente).
- Crie seu repositório com “git init” (ou use o repositório existente).
Adicionar um controle remoto com init
git remote add init git://github.com/picanteverde/init.git
Obtenha o ramo que você deseja
git pull init usermanager
Obtenha o arquivo de processo do Heroku
git pull init heroku-webprocess
Com o Heroku Toolbelt instalado, crie um aplicativo Heroku
heroku create
Envie seu branch master para o Heroku
git push heroku master
- Visite seu aplicativo, instalado e funcionando no Heroku!
Agora você pode começar a desenvolver seu recurso matador com apenas algumas linhas de código. Não apenas isso, mas você estará desenvolvendo com as tecnologias mais recentes e eficientes em um conjunto de desenvolvimento tão automatizado quanto possível.
Espero que você possa usar o Init para iniciar sua próxima grande ideia. Lembre-se de verificar o repositório do Init para novas correções e recursos - seu desenvolvimento está muito ativo e estou ansioso para ouvir seus comentários.