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. 4.
  2. 5.

    DEV

  3. 6.
  4. 7.
  5. 9.

    =)

  6. 14.
  7. 15.
  8. 34.

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

    formulário com muitas informações 12%
  9. 48.
  10. 50.
  11. 52.
  12. 55.
  13. 63.
  14. 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;
  15. 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; }
  16. 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"); }
  17. 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; }
  18. 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); }
  19. 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;
  20. 78.
  21. 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); }
  22. 84.
  23. 85.
  24. 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); }
  25. 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 // --------------
  26. 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); }); });
  27. 92.
  28. 95.
  29. 96.
  30. 97.
  31. 98.
  32. 99.
  33. 100.
  34. 101.
  35. 102.
  36. 103.
  37. 104.
  38. 105.
  39. 106.
  40. 107.
  41. 108.
  42. 109.
  43. 110.
  44. 111.
  45. 112.
  46. 116.
  47. 117.
  48. 118.
  49. 119.
  50. 120.
  51. 121.
  52. 122.
  53. 123.
  54. 126.
  55. 128.
  56. 132.
  57. 134.
  58. 136.
  59. 137.
  60. 138.
  61. 139.
  62. 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>
  63. 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; }
  64. 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.
  65. 152.

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

    e passa a perguntar "como vou fazer isso da melhor maneira"...
  66. 155.