Desafios do Desenvolvimento de Front-end em um e-commerce

Desafios do Desenvolvimento de Front-end em um e-commerce

Quando o objetivo é vender, uma modificação mínima pode impactar a taxa de conversão final. Para que um e-commerce atinja sua performance máxima, é necessário fazer com que os componentes, a equipe e mínimos detalhes funcionem em perfeita harmonia -- e o front-end é um deles.

Nesta palestra, mostrei os desafios enfrentados pelo time de engenharia de Front-end da Baby.com.br: como trabalhar com uma equipe com vários desenvolvedores, gerando componentes auto-contidos, testáveis e escaláveis, mantendo a melhor performance possível, sem perder o padrão de qualidade.

Fonte das métricas: http://blog.bizelo.com/blog/2012/10/18/infographic-shopping-cart-abandonment-rates/

Fa59eb5159c978e2577e65f45fa9171f?s=128

Eduardo Shiota Yasuda

May 03, 2013
Tweet

Transcript

  1. os desafios do desenvolvimento de front-end em um e-commerce @shiota

    2013
  2. olá! speakerdeck.com/eshiota github.com/eshiota @shiota

  3. front-end engineer @

  4. None
  5. DEV

  6. None
  7. None
  8. e-commerce 101 em alguns slides

  9. =)

  10. taxa de conversão dos usuários que entram no site, quantos

    finalizam uma compra?
  11. ticket médio em média, quanto os usuários gastam por compra?

  12. =) taxa de conversão × ticket médio =

  13. taxa de conversão × ticket médio = $

  14. = $ =)

  15. = ? =)

  16. complexo indeciso exigente inexperiente decidido cuidadoso experiente

  17. ser humano, como todos nós =)

  18. o que faz o usuário abandonar o carrinho?

  19. alto custo de frete 44% $

  20. não estão prontos para finalizar ? 41%

  21. produtos muito caros 25% $

  22. guardam para depois 24%

  23. não mencionou claramente o frete 22% ?

  24. sem guest checkout 14%

  25. formulário com muitas informações 12%

  26. checkout complexo 11%

  27. website lento 11%

  28. taxas extras 8%

  29. falta de opções de pagamento 7%

  30. entrega demorada 6%

  31. spam de ofertas 6%

  32. site não funciona 5% =(

  33. como o front-end pode melhorar a conversão?

  34. site não funciona 5% website lento 11% checkout complexo 11%

    formulário com muitas informações 12%
  35. velocidade da página interface estável detalhes = emotion design validação

    de novas hipóteses
  36. desafios de front-end (agora a palestra começa =P)

  37. múltiplos desenvolvedores desenvolvimento escalável performance client-side testes a/b

  38. trabalhando com vários desenvolvedores

  39. trabalhar em equipe é difícil... =(

  40. aspas simples × aspas duplas

  41. ponto e vírgula no JS × sem ponto e vírgula

  42. JavaScript × CoffeeScript

  43. (JavaScript, claro)

  44. (com ponto e vírgula)

  45. ... mas cada um pode ter uma visão diferente e

    complementar. =)
  46. code smell performance sintaxe arquitetura

  47. mantenha um code standard para o time.

  48. None
  49. consistência, legibilidade, sem bikeshed.

  50. None
  51. git + pull requests

  52. None
  53. qualquer um revisa, qualquer um comenta.

  54. diferentes visões, mais erros detectados.

  55. None
  56. o conhecimento é disseminado pelo time.

  57. todos ficam mais criteriosos com o que fazem.

  58. desenvolvimento escalável e testável

  59. desenvolvimento ágil: mudanças precisas, altos ganhos.

  60. melhorias são constantes, e nada é 100% definitivo.

  61. o código deve ser facilmente alterável/adaptável.

  62. dica 1 usem pre-processors de CSS. sério. agora. já. eu

    espero.
  63. sass +

  64. variáveis, mixins e funções

  65. /********************************************************************* * * Variables Module * * All constants that

    will be used through the styles must be * defined here. * *********************************************************************/ $TEXT_COLOR : #555; $DISCOUNT_COLOR : #ef6565; $LIGHT_COLOR : #fefafa; $SELECTION_BACKGROUND : #41bdce; $SELECTION_COLOR : #fff; $LINK_COLOR : #447f87; $LINK_HOVER_COLOR : #41bdce; $LINK_ACTIVE_COLOR : #447f87; $ERROR_BACKGROUND : #fffaad; $LIGHT_BACKGROUND : #fefefa; $SITE_WIDTH : 978px; $FOOTER_HEIGHT : 777px; $PURPLE : #905194; $ORANGE : #fbb100;
  66. /********************************************************************* * * Mixins Module * * All general purpose

    mixins are defined here. * *********************************************************************/ /********************************************************************* * =Clearfix *********************************************************************/ @mixin clearfix { &:after { clear: both; content: " "; display: block; font-size: 0; height: 0; visibility: hidden; } zoom: 1; } /********************************************************************* * =Image replacement * * `display` property should be declare on the element, not here * on the mixin. Element must have fixed width and height. *********************************************************************/ @mixin img_replacement { text-indent: 100%; overflow: hidden; white-space: nowrap; }
  67. /********************************************************************* * * Functions Module * * Custom functions used

    by the application * *********************************************************************/ // Returns unitless number @function remove-unit($number) { $unit: unit($number); $one: 1; @if $unit == "px" { $one: 1px; } @if $unit == "em" { $one: 1em; } @if $unit == "%" { $one: 1%; } @return $number / $one; } // Returns flexible value // Returns `em` by default, accepts `%` as format. @function flex($target, $context: 14, $unit: "em") { $size: remove-unit($target) / remove-unit($context); @if $unit == "em" { @return #{$size}em; } @if $unit == "%" { @return percentage($size); } } // Alias to `flex` function, using `%` as format. @function perc($target, $context) { @return flex($target, $context, "%"); } // Alias to `flex` function, using `em` as format. @function em($target, $context: 14) { @return flex($target, $context, "em"); }
  68. estilos modularizados em partials

  69. app/ assets/ stylesheets/ base/ _functions.scss _mixins.scss _variables.scss ui/ _breadcrumb.scss _carousel.scss

    _dentedBox.scss _flashMessage.scss
  70. /******************************************************************************* * * UI > Breadcrumb * * General styles

    for the breadcrumb. * *******************************************************************************/ .breadcrumb { font-size: em(12px); line-height: em(21px, 12px); text-transform: uppercase; color: #444; width: 978px; } .breadcrumb a, .breadcrumb a:visited, .breadcrumb a:active { color: #444; text-decoration: none; } .breadcrumb a:hover { color: #444; text-decoration: underline; } .breadcrumb .separator { padding: 0 3px; }
  71. /******************************************************************************* * * UI > Loader * * Animated loader

    for AJAX requests * *******************************************************************************/ @mixin loader_sprite_position($xoffset, $yoffset) { background-position: sprite-position($icon-sprite, loader_sprite, $xoffset, $yoffset); } .loader { width: 25px; height: 25px; display: none; } .loader b { display: block; width: 25px; height: 25px; background-image: sprite-url($icon-sprite); } .loader b, .loader .f1 { @include loader_sprite_position(-10px, -10px); } .loader .f2 { @include loader_sprite_position(-45px, -10px); } .loader .f3 { @include loader_sprite_position(-80px, -10px); } .loader .f4 { @include loader_sprite_position(-115px, -10px); } .loader .f5 { @include loader_sprite_position(-150px, -10px); } .loader .f6 { @include loader_sprite_position(-185px, -10px); } .loader .f7 { @include loader_sprite_position(-220px, -10px); } .loader .f8 { @include loader_sprite_position(-255px, -10px); }
  72. geração automática de sprites acelera o desenvolvimento.

  73. $icon-sprite: sprite-map("icon/*.png", $spacing: 16px, $repeat: no-repeat, $layout: vertical);

  74. $icon-sprite: sprite-map("icon/*.png", $spacing: 16px, $repeat: no-repeat, $layout: vertical);

  75. /* Compass sprite function receives the map variable and image

    as arguments */ background: sprite($icon-sprite, arrow_dropdown) no-repeat; /* Compiled CSS */ background: url(/assets/icon-s5dab8c2901.png) -40px -158px no-repeat;
  76. função de inline image economiza requests.

  77. /* Compiled CSS */ background: #f5f3fb url('data:image/ png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQCAMAAAAcVM5PAAAAGXRFWHRTb2Z0d2FyZQBBZG9 iZSBJbWFnZVJlYWR5ccllPAAAAAlQTFRF5+TW////////4qZUpQAAAAN0Uk5T// 8A18oNQQAAACBJREFUeNpiYGBgAgMGBkYog4mJXAbILAiDkVxzAAIMAEMOAPMId2OWAAAAAElFTkSuQmCC

    ') repeat; /* Generates a base64 image */ background: #f5f3eb inline-image("bg_dots.png") repeat;
  78. None
  79. (seja criterioso)

  80. dica 2 módulos: poucas linhas, comportamentos isolados e extensíveis.

  81. estrutura base (reset, base styles) grid padrões módulos módulos contextualizados

  82. css do módulo

  83. /******************************************************************************* * * UI > Loader * * Animated loader

    for AJAX requests * *******************************************************************************/ @mixin loader_sprite_position($xoffset, $yoffset) { background-position: sprite-position($icon-sprite, loader_sprite, $xoffset, $yoffset); } .loader { width: 25px; height: 25px; display: none; } .loader b { display: block; width: 25px; height: 25px; background-image: sprite-url($icon-sprite); } .loader b, .loader .f1 { @include loader_sprite_position(-10px, -10px); } .loader .f2 { @include loader_sprite_position(-45px, -10px); } .loader .f3 { @include loader_sprite_position(-80px, -10px); } .loader .f4 { @include loader_sprite_position(-115px, -10px); } .loader .f5 { @include loader_sprite_position(-150px, -10px); } .loader .f6 { @include loader_sprite_position(-185px, -10px); } .loader .f7 { @include loader_sprite_position(-220px, -10px); } .loader .f8 { @include loader_sprite_position(-255px, -10px); }
  84. None
  85. None
  86. css do módulo contextualizado

  87. // On ui/_buttons.scss .bt-wrapper .loader { position: absolute; z-index: 4;

    right: 20px; top: 50%; margin-top: -9px; } // On modules/_checkoutAddressForm.scss .address-form .cep-input .loader { position: absolute; right: -33px; top: em(29px); }
  88. javascript enxuto, auto-contido.

  89. // Implements the animated loader for AJAX requests // Loader

    constructor // // * `placement`: Function that determines the loader's placement ns("EDEN.ui.Loader", function (placement) { if (!(this instanceof EDEN.ui.Loader)) { return new EDEN.ui.Loader(placement); } this.frame = 1; this.framesQty = 8; this.stack = []; this.animating = false; this.$loader = $("<div class='loader'><b> </b></div>"); this.$renderer = this.$loader.find("b"); this.placement = placement; this.init(); }); EDEN.ui.Loader.prototype = { // Properties // ---------- // Animation speed (in frames per second) fps : 20, // Fading speed fadeSpeed : 150, // Public methods // --------------
  90. testável!

  91. describe("EDEN.ui.Loader", function () { var Loader = EDEN.ui.Loader; beforeEach(function ()

    { loadFixtures("loader.html"); }); afterEach(function () { $("body").find(".loader").remove(); }); it("accepts instance creation without new operator", function () { var newLoader = Loader(); expect(newLoader).toBeInstanceOf(Loader); }); it("inits the loader on creation", function () { var loader , oldInit = EDEN.ui.Loader.prototype.init ; EDEN.ui.Loader.prototype.init = jasmine.createSpy(); loader = new Loader(); expect(loader.init).toHaveBeenCalled(); EDEN.ui.Loader.prototype.init = oldInit; }); it("appends the loader to body as a default", function () { var loader = new Loader(); expect($("body").find(".loader").length).toEqual(1); }); it("appends the loader through an argument function", function () { var loader = new Loader(function ($loader) { $("#loader-placeholder").append($loader); }); expect($("#loader-placeholder").find(".loader").length).toEqual(1); }); });
  92. None
  93. dica 3 javascript desacoplado e modularizado

  94. mediator: ponto central de comunicação via pub/sub

  95. None
  96. None
  97. None
  98. None
  99. None
  100. None
  101. None
  102. None
  103. None
  104. None
  105. None
  106. None
  107. None
  108. None
  109. None
  110. None
  111. None
  112. None
  113. cartReviewModule.js checkoutModule.js Cart.js CartView.js CartProxy.js installmentSelector.js couponForm.js ShippingAddressSelector.js AddressSelector.js

  114. appMediator.js

  115. cartReviewModule.js checkoutModule.js Cart.js installmentSelector.js couponForm.js ShippingAddressSelector.js appMediator.js

  116. None
  117. None
  118. None
  119. None
  120. None
  121. None
  122. None
  123. None
  124. você não precisa saber tudo isso de primeira.

  125. addyosmani.com/largescalejavascript

  126. None
  127. aprenda javascript antes de se focar em um framework.

  128. None
  129. performance client-side

  130. css/javascript minification/compression

  131. lazy-load everything! o/

  132. None
  133. sprites e imagens inlines

  134. sass +

  135. não abuse de font-faces

  136. None
  137. testes a/b

  138. None
  139. None
  140. isole os estilos e JS em classes, partials e módulos

    totalmente separados
  141. <nav id="site-menu" class="site-menu"> <div class="site-menu-container"> <% if new_header? %> <%=

    render "layouts/open_site_nav" %> <% else %> <%= render "layouts/site_nav" %> <% end %> <% unless new_header? %> <%= render "layouts/search" %> <% end %> </div> </nav>
  142. /******************************************************************************* * =Menu A *******************************************************************************/ .site-header-old .user-menu { position: absolute;

    right: perc(261px, $SITE_WIDTH); cursor: pointer; width: 213px; height: 63px; overflow: hidden; z-index: 600; } /******************************************************************************* * =Menu B *******************************************************************************/ .site-header-new .user-menu { position: absolute; right: perc(261px, $SITE_WIDTH); width: perc(150px, $SITE_WIDTH); height: em(63px); overflow: hidden; z-index: 600; }
  143. AB-TESTING.md - como remover a versão perdedora

  144. # A/B Testing on Baby Site This document lists all

    A/B tests currently being run on the project, and shortly introduces the method being used. ## Tests currently being run ### Site-wide #### Header design version * Test name: `header-version` * Starts at: `ApplicationController`, on `:before_filter` * Goal: When user goes to a success checkout page * Ends at: `orders#success` view * PR/Commits: [#664](https://github.com/Baby-com-br/troy/pull/664) To remove this test: * Remove the `new_header?` method and its `:helper_method` on `application_controller.rb` * Remove the `header_version` method and its `:helper_method` on `application_controller.rb` and ALL its calls. * Consolidate the correct `render` calls on `layouts/_header.html.erb` and `layouts/_site_menu.html.erb` * Remove the `site-header-<%= header_version %>` class on `layouts/_header.html.erb` * Remove the `header-version-<%= header_version %>` class on `layouts/_head.html.erb`, on the `<body>` tag * Remove the `finished` call on `baby-site/app/views/orders/success.html.erb` * On `modules/_mainSearchForm.scss`, remove the entire block related to the loser version, and on the winner version: (1) remove the comment header about the A/B test, (2) unprefix all selectors by removing either `.site-menu` (if the old header won) or `.site-header` (if the new header won) * On `layout/_user_menu.scss`, remove the entire block related to the loser version, and on the winner version: (1) remove the comment header about the A/B test, (2) unprefix all selectors by removing either `.site-header-new` (if the old header won) or `.site-header-old` (if the new header won) * On `ui/_section_header.scss`, remove the `.header-version-old .section-titles` and `.header-version-new .section-titles` blocks, and use the winner padding on `.section-titles`. * On `sections/_profile.scss`, remove the `.header-version-old .profile-header .site-menu` and `.header-version-new .profile-header .site-menu` blocks, and use the winner padding on `.profile-header .site-menu`. * On `layout/_main.scss`, delete the `.header-version-old .site-menu-container` block.
  145. shiota, um dev front-end precisa saber back-end?

  146. fulano(a), eu preciso saber cozinhar ou lavar roupa?

  147. não, mas ajuda, né? ;D

  148. você não precisa ser um nando vieira.

  149. saber back-end melhora seu código.

  150. saber back-end lhe dá mais controle.

  151. saber back-end melhora a comunicação.

  152. quando você deixa de perguntar apenas "como vou fazer isso"

    e passa a perguntar "como vou fazer isso da melhor maneira"...
  153. ... você está no caminho certo.

  154. divirta-se. sempre. =)

  155. None
  156. obrigado! speakerdeck.com/eshiota github.com/eshiota @shiota