Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Pague o aluguel - Trabalhando efetivamente com código em débito técnico - Nelson Senna

55 views

Published on

Dívidas são algo cotidiano. Empresas e pessoas contraem dívidas para operar seus negócios ou realizar sonhos e, com código é a mesma coisa. Vez ou outra admitimos algo fora dos padrões de qualidade para corrigir algum problema ou ter alguma vantagem sobre a concorrência.
O grande problema é quando essa nossa dívida sai do controle gerando prejuízos não só para a empresa mas, nós desenvolvedores. A falta de controle dessa dívida tem como maior efeito software rígido, difícil de entender e mudar, tornando-o mais suscetível a bugs. Para pagar o que devemos, temos que reverter esse quadro aumentando a facilidade em entender o código. Como fazer isso? Usando bons nomes!

Published in: Software
  • Be the first to comment

Pague o aluguel - Trabalhando efetivamente com código em débito técnico - Nelson Senna

  1. 1. Pague o aluguel Trabalhando efetivamente com código em débito
  2. 2. Eu sou… • Desenvolvedor; • PHP, Ruby, Python e JavaScript. Um pouco de Java e C# (Xamarin); • Trabalho na BriteCore; • Trabalho remotamente desde 2016; • @nelson_senna
  3. 3. Dívida
  4. 4. “…fazendo as coisas de maneira rápida e suja cria-se um débito técnico…” –Martin Fowler
  5. 5. Débito técnico é… Código usado para protótipos, provas de conceito ou até uma pequena correção.
  6. 6. Classificação de débito técnico Imprudente Prudente Deliberado Negligente “Não temos tempo!” “Precisamos entregar, depois vemos isso.” “O que é OOD? OOP? Design?” “Agora sabemos que devíamos ter feito isso.”
  7. 7. Débito técnico é… Código que deveríamos jogar fora…
  8. 8. –Martin Fowler “Como uma dívida financeira, o débito técnico incorre em pagamentos de juros, que vêm na forma de esforço extra que temos que fazer no futuro...”
  9. 9. Efeitos da dívida
  10. 10. “…esse código muitas vezes ganha vida própria… Funciona, então por que consertar?” –Brian Foote
  11. 11. E algum tempo e “ifs” depois…
  12. 12. “Um Big Ball of Mud tem estrutura desordenada, espalhada, desleixada, remendada e, código espaguete” –Brian Foote
  13. 13. Big Ball of Mud • Os nomes de variáveis e funções pouco informativos ou enganosos • Uso extensivo de variáveis globais • Métodos com longas listas de parâmetros mal definidos • Métodos longos e complicados e, executam várias tarefas não relacionadas • Código duplicado • O fluxo de controle é difícil de entender e difícil de seguir • A intenção do programador é quase impossível de discernir • O código é simplesmente ilegível e espalhado sem modularização clara • O código exibe os sinais inconfundíveis de patch após patch nas mãos de vários mantenedores, cada um dos quais mal entendeu as conseqüências do que estava fazendo • Sem documentação
  14. 14. Big Ball of Mud A estrutura rígida que faz com que o código fique difícil de entender, caro e suscetível a erros.
  15. 15. “Da sinonímia dos termos organização e conhecimento, retira-se que a síntese mais produtiva, ou mais instigadora, para a construção de uma idéia acerca da organização do conhecimento na sociedade é aquela que abstrai de organização, pelo verbo organizar, os sentidos de ORGANIZAR, que são os seguintes: estabelecer as bases de; arrumar de determinado modo; colocar em certa ordem.” –Renato Rocha Souza
  16. 16. Entender é… Organizar ideias e conceitos, atribuindo uma ordem, classificando os mesmos.
  17. 17. Então… if (organizacao) { entendimento++; bigBallOfMud--; }
  18. 18. Pagando a dívida
  19. 19. Entender é… Organizar ideias e conceitos, atribuindo uma ordem, classificando os mesmos.
  20. 20. Nome é… Palavra ou locução com que se designa uma classe de coisas, pessoas, animais, um lugar, um acidente geográfico, um astro etc.
  21. 21. “Existem duas coisas difíceis em computação: invalidação de cache e nomear coisas.” –Phil Karlton
  22. 22. Nomes são… DESCOBERTOS!
  23. 23. Nomes são descobertos! Sem nome Sem sentido Honesto Honesto e Completo Faz o que o nome diz Intenção ↪ ↪ ↪ ↪ ↪ ↪ Abstração de domínio
  24. 24. Nomes são descobertos! Sem nome Sem sentido Honesto Honesto e Completo Faz o que o nome diz Intenção ↪ ↪ ↪ ↪ ↪ ↪ Abstração de domínio @arlobelshee
  25. 25. var rootJquery, // A simple way to check for HTML strings // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) // Shortcut simple #id case for speed rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]+))$/, init = jQuery.fn.init = function( selector, context, root ) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) if (!selector) { return this; } // Method init() accepts an alternate rootjQuery // so migrate can support jQuery.sub (gh-2101) root = root || rootjQuery; // Handle HTML strings if (typeof selector === "string") { if (selector[0] === "<" && selector[selector.length - 1] === ">" && selector.length >= 3) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [null, selector, null]; } else { match = rquickExpr.exec( selector ); } … // HANDLE: $(expr, $(...)) } else if (!context || context.jquery) { return (context || root).find(selector); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor(context).find(selector); } // HANDLE: $(DOMElement) } else if (selector.nodeType) { this[0] = selector; this.length = 1; return this; // HANDLE: $(function) // Shortcut for document ready } else if (isFunction(selector)) { return root.ready !== undefined ? root.ready(selector) : // Execute immediately if ready is not present selector(jQuery); } return jQuery.makeArray(selector, this); }; // Match html or make sure no context is specified for #id if (match && (match[1] || !context)) { // HANDLE: $(html) -> $(array) if (match[1]) { context = context instanceof jQuery ? context[0] : context; // Option to run scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge(this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true )); // HANDLE: $(html, props) if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { for (match in context) { // Properties of context are called as methods if possible if (isFunction(this[match])) { this[match](context[match]); // ...and otherwise set as attributes } else { this.attr(match, context[match]); } } } return this; // HANDLE: $(#id) } else { elem = document.getElementById(match[2]); if (elem) { // Inject the element directly into the jQuery object this[0] = elem; this.length = 1; } return this; }
  26. 26. var rootJquery, // A simple way to check for HTML strings // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) // Shortcut simple #id case for speed rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]+))$/, init = jQuery.fn.init = function( selector, context, root ) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) if (!selector) { return this; } // Method init() accepts an alternate rootjQuery // so migrate can support jQuery.sub (gh-2101) root = root || rootjQuery; // Handle HTML strings if (typeof selector === "string") { if (selector[0] === "<" && selector[selector.length - 1] === ">" && selector.length >= 3) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [null, selector, null]; } else { match = rquickExpr.exec( selector ); } … // HANDLE: $(expr, $(...)) } else if (!context || context.jquery) { return (context || root).find(selector); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor(context).find(selector); } // HANDLE: $(DOMElement) } else if (selector.nodeType) { this[0] = selector; this.length = 1; return this; // HANDLE: $(function) // Shortcut for document ready } else if (isFunction(selector)) { return root.ready !== undefined ? root.ready(selector) : // Execute immediately if ready is not present selector(jQuery); } return jQuery.makeArray(selector, this); }; // Match html or make sure no context is specified for #id if (match && (match[1] || !context)) { // HANDLE: $(html) -> $(array) if (match[1]) { context = context instanceof jQuery ? context[0] : context; // Option to run scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge(this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true )); // HANDLE: $(html, props) if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { for (match in context) { // Properties of context are called as methods if possible if (isFunction(this[match])) { this[match](context[match]); // ...and otherwise set as attributes } else { this.attr(match, context[match]); } } } return this; // HANDLE: $(#id) } else { elem = document.getElementById(match[2]); if (elem) { // Inject the element directly into the jQuery object this[0] = elem; this.length = 1; } return this; }
  27. 27. jQuery( selector [, context ] ) jQuery( element ) jQuery( elementArray ) jQuery( object ) jQuery( selection ) jQuery() jQuery( html [, ownerDocument ] ) jQuery( html, attributes ) jQuery( callback )
  28. 28. var rootJquery, // A simple way to check for HTML strings // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) // Shortcut simple #id case for speed rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]+))$/, init = jQuery.fn.init = function( selector, context, root ) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) if (!selector) { return this; } // Method init() accepts an alternate rootjQuery // so migrate can support jQuery.sub (gh-2101) root = root || rootjQuery; // Handle HTML strings if (typeof selector === "string") { if (selector[0] === "<" && selector[selector.length - 1] === ">" && selector.length >= 3) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [null, selector, null]; } else { match = rquickExpr.exec( selector ); } … // HANDLE: $(expr, $(...)) } else if (!context || context.jquery) { return (context || root).find(selector); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor(context).find(selector); } // HANDLE: $(DOMElement) } else if (selector.nodeType) { this[0] = selector; this.length = 1; return this; // HANDLE: $(function) // Shortcut for document ready } else if (isFunction(selector)) { return root.ready !== undefined ? root.ready(selector) : // Execute immediately if ready is not present selector(jQuery); } return jQuery.makeArray(selector, this); }; // Match html or make sure no context is specified for #id if (match && (match[1] || !context)) { // HANDLE: $(html) -> $(array) if (match[1]) { context = context instanceof jQuery ? context[0] : context; // Option to run scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge(this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true )); // HANDLE: $(html, props) if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { for (match in context) { // Properties of context are called as methods if possible if (isFunction(this[match])) { this[match](context[match]); // ...and otherwise set as attributes } else { this.attr(match, context[match]); } } } return this; // HANDLE: $(#id) } else { elem = document.getElementById(match[2]); if (elem) { // Inject the element directly into the jQuery object this[0] = elem; this.length = 1; } return this; } Sem nome!
  29. 29. var handleHTMLStrings = function (selector, context, root) { if (selector[0] === "<" && selector[selector.length - 1] === ">" && selector.length >= 3) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [null, selector, null]; } else { match = rquickExpr.exec( selector ); } // Match html or make sure no context is specified for #id if (match && (match[1] || !context)) { // HANDLE: $(html) -> $(array) if (match[1]) { // ... // HANDLE: $(html, props) if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { // ... } return this; // HANDLE: $(#id) } else { // ... } // HANDLE: $(expr, $(...)) } else if (!context || context.jquery) { // ... // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { // ... } } Sem sentido!
  30. 30. var handleHTMLStrings = function (selector, context, root) { if (selector[0] === "<" && selector[selector.length - 1] === ">" && selector.length >= 3) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [null, selector, null]; } else { match = rquickExpr.exec( selector ); } // Match html or make sure no context is specified for #id if (match && (match[1] || !context)) { // HANDLE: $(html) -> $(array) if (match[1]) { // ... // HANDLE: $(html, props) if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { // ... } return this; // HANDLE: $(#id) } else { // ... } // HANDLE: $(expr, $(...)) } else if (!context || context.jquery) { // ... // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { // ... } }
  31. 31. var handleHTMLStringsAndElementIds = function (selector, context, root) { if (selector[0] === "<" && selector[selector.length - 1] === ">" && selector.length >= 3) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [null, selector, null]; } else { match = rquickExpr.exec( selector ); } // Match html or make sure no context is specified for #id if (match && (match[1] || !context)) { // HANDLE: $(html) -> $(array) if (match[1]) { // ... // HANDLE: $(html, props) if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { // ... } return this; // HANDLE: $(#id) } else { // ... } // HANDLE: $(expr, $(...)) } else if (!context || context.jquery) { // ... // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { // ... } } Honesto!
  32. 32. var handleHTMLStringsAndElementIds = function (selector, context, root) { if (selector[0] === "<" && selector[selector.length - 1] === ">" && selector.length >= 3) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [null, selector, null]; } else { match = rquickExpr.exec( selector ); } // Match html or make sure no context is specified for #id if (match && (match[1] || !context)) { // HANDLE: $(html) -> $(array) if (match[1]) { // ... // HANDLE: $(html, props) if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { // ... } return this; // HANDLE: $(#id) } else { // ... } // HANDLE: $(expr, $(...)) } else if (!context || context.jquery) { // ... // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { // ... } }
  33. 33. var handleHTMLStringsAndElementIdsAndCSSSelectors = function (selector, context, root) { if (selector[0] === "<" && selector[selector.length - 1] === ">" && selector.length >= 3) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [null, selector, null]; } else { match = rquickExpr.exec( selector ); } // Match html or make sure no context is specified for #id if (match && (match[1] || !context)) { // HANDLE: $(html) -> $(array) if (match[1]) { // ... // HANDLE: $(html, props) if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { // ... } return this; // HANDLE: $(#id) } else { // ... } // HANDLE: $(expr, $(...)) } else if (!context || context.jquery) { // ... // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { // ... } } Honesto e completo!
  34. 34. handleHTMLStringsAndElementIdsAndCSSSelectors = function (selector, context, root)
  35. 35. handleHTMLStringsAndElementIdsAndCSSSelectors = function (selector, context, root) handleHTMLStrings = function (selector, context, root)
  36. 36. handleHTMLStringsAndElementIdsAndCSSSelectors = function (selector, context, root) handleHTMLStrings = function (selector, context, root) handleElementIds = function (selector, context, root)
  37. 37. handleHTMLStringsAndElementIdsAndCSSSelectors = function (selector, context, root) handleHTMLStrings = function (selector, context, root) handleElementIds = function (selector, context, root) handleCSSSelectors = function (selector, context, root)
  38. 38. handleHTMLStringsAndElementIdsAndCSSSelectors = function (selector, context, root) handleHTMLStrings = function (selector, context, root) handleElementIds = function (selector, context, root) handleCSSSelectors = function (selector, context, root)
  39. 39. var handleHTMLStrings = function (match, context, root) { // Match html or make sure no context is specified for #id if (match && (match[1] || !context)) { // HANDLE: $(html) -> $(array) if (match[1]) { context = context instanceof jQuery ? context[0] : context; // Option to run scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge(this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true )); // HANDLE: $(html, props) if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { for (match in context) { // Properties of context are called as methods if possible if (isFunction(this[match])) { this[match](context[match]); // ...and otherwise set as attributes } else { this.attr(match, context[match]); } } } return this; } } } Sem sentido?
  40. 40. Características de bons nomes de métodos • Use verbos*; • Sem abreviações; • Conciso (mas, não muito!); • Use perguntas quando o método retornar um boolean (isEmpty(), canDo()); • Não use “And” e “Or” nos seus nomes; • Não deixe o nome redundante com o argumento
 ($list->addItem($item)); • Não deixe o nome redundante com quem chama
 ($list->addToList($item));
  41. 41. var buildFromHTMLString = function (match, context, root) { // Match html or make sure no context is specified for #id if (match && (match[1] || !context)) { // HANDLE: $(html) -> $(array) if (match[1]) { context = context instanceof jQuery ? context[0] : context; // Option to run scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge(this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true )); // HANDLE: $(html, props) if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { for (match in context) { // Properties of context are called as methods if possible if (isFunction(this[match])) { this[match](context[match]); // ...and otherwise set as attributes } else { this.attr(match, context[match]); } } } return this; } } }
  42. 42. Temos um bom nome? • Use verbos* √ • Sem abreviações √ • Conciso (mas, não muito!) √ • Use perguntas quando o método retornar um boolean (isEmpty(), canDo()) √ • Não use “And” e “Or” nos seus nomes √ • Não deixe o nome redundante com o argumento √ • Não deixe o nome redundante com quem chama √
  43. 43. var fromHTMLString = function (match, context) { // HANDLE: $(html) -> $(array) if (match[1]) { context = context instanceof jQuery ? context[0] : context; // Option to run scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge(this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true )); // HANDLE: $(html, props) if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { for (match in context) { // Properties of context are called as methods if possible if (isFunction(this[match])) { this[match](context[match]); // ...and otherwise set as attributes } else { this.attr(match, context[match]); } } } return this; } }
  44. 44. var fromHTMLString = function (match, context) { // HANDLE: $(html) -> $(array) if (match[1]) { context = context instanceof jQuery ? context[0] : context; // Option to run scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present jQuery.merge(this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true )); // HANDLE: $(html, props) if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) { for (match in context) { // Properties of context are called as methods if possible if (isFunction(this[match])) { this[match](context[match]); // ...and otherwise set as attributes } else { this.attr(match, context[match]); } } } return this; } }
  45. 45. Características de bons nomes de variáveis • Sem abreviações; • Conciso (mas, não muito!); • Use perguntas quando o método retornar um boolean (isEmpty, canDo); • Não use “And” e “Or” nos seus nomes;
  46. 46. var fromHTMLString = function (source, context) { if (source) { context = context instanceof jQuery ? context[0] : context; var runScriptsForBackCompat = true var isNode = context && context.nodeType; var contextDocument = isNode ? context.ownerDocument || context : document; jQuery.merge(this, jQuery.parseHTML(source, contextDocument, runScriptsForBackCompat)); var isSingleTagWithoutAttributes = rsingleTag.test(source) var isAttributesSuperSet = jQuery.isPlainObject(context); if (isSingleTagWithoutAttributes && isAttributesSuperSet) { for (attributeName in context) { // Properties of context are called as methods if possible if (isFunction(this[attributeName])) { this[attributeName](context[attributeName]); // ...and otherwise set as attributes } else { this.attr(attributeName, context[attributeName]); } } } return this; } }
  47. 47. var fromHTMLString = function (source, context) { if (source) { context = context instanceof jQuery ? context[0] : context; var runScriptsForBackCompat = true var isNode = context && context.nodeType; var contextDocument = isNode ? context.ownerDocument || context : document; jQuery.merge(this, jQuery.parseHTML(source, contextDocument, runScriptsForBackCompat)); var isSingleTagWithoutAttributes = rsingleTag.test(source) var isAttributesSuperSet = jQuery.isPlainObject(context); if (isSingleTagWithoutAttributes && isAttributesSuperSet) { for (attributeName in context) { // Properties of context are called as methods if possible if (isFunction(this[attributeName])) { this[attributeName](context[attributeName]); // ...and otherwise set as attributes } else { this.attr(attributeName, context[attributeName]); } } } return this; } }
  48. 48. Características de bons nomes de classes • Use substantivos e adjetivos*; • Não use muitos adjetivos (AbstractFactoryPatternBase); • Não use “Manager”, “Helper” e “Data” nos seus nomes; • Não use Sufixos do seu namespace (Service, Factory, Iterator) • Evite usar “er”, “or”, “tion”, “sion” (ObjectFinder, DataProcessor, Conversion, DataManipulation)
  49. 49. –Chris Oldwood “So, a "DateTimeProvider", that's basically just a clock, right?”
  50. 50. Características de bons nomes de variáveis Service is the new Enzo
  51. 51. var HTMLCreationContext = function (elementOrAttributes) { this.elementOrAttributes = elementOrAttributes; } HTMLCreationContext.prototype = { isNode: function () { return (this.elementOrAttributes && this.elementOrAttributes.nodeType); }, getOwnerDocument: function () { if (this.isNode) { return this.elementOrAttributes.ownerDocument || this.elementOrAttributes; } return document; }, getAttributesSuperSet: function () { if (jQuery.isPlainObject(this.elementOrAttributes)) { return this.elementOrAttributes; } return {}; }, getMergeContext: function () { if (this.elementOrAttributes instanceof jQuery) { return this.elementOrAttributes[0]; } return this.elementOrAttributes; } }
  52. 52. var HTMLCreationContext = function (elementOrAttributes) { this.elementOrAttributes = elementOrAttributes; } HTMLCreationContext.prototype = { isNode: function () { return (this.elementOrAttributes && this.elementOrAttributes.nodeType); }, getOwnerDocument: function () { if (this.isNode) { return this.elementOrAttributes.ownerDocument || this.elementOrAttributes; } return document; }, getAttributesSuperSet: function () { if (jQuery.isPlainObject(this.elementOrAttributes)) { return this.elementOrAttributes; } return {}; }, getMergeContext: function () { if (this.elementOrAttributes instanceof jQuery) { return this.elementOrAttributes[0]; } return this.elementOrAttributes; } }
  53. 53. var fromHTMLString = function (source, context) { if (source) { var creationContext = new HTMLCreationContext(context); var runScriptsForBackCompat = true jQuery.merge(this, jQuery.parseHTML( source, creationContext.getOwnerDocument(), runScriptsForBackCompat )); var isSingleTagWithoutAttributes = rsingleTag.test(source) if (isSingleTagWithoutAttributes) { var attributes = creationContext.getAttributesSuperSet(); for (attributeName in attributes) { // Properties of context are called as methods if possible if (isFunction(this[attributeName])) { this[attributeName](context[attributeName]); // ...and otherwise set as attributes } else { this.attr(attributeName, context[attributeName]); } } } return this; } }
  54. 54. Concluindo… 1. Procure por lugares com mais de um nível de identação 2. Extraia e dê nome para o que fizer sentido 3. Descreva o que o método realmente faz, sem vergonha, seja honesto! 4. Vá diminuindo o nome do método extraindo suas responsabilidades 5. Excesso de métodos privados para um dado contexto: Uma nova classe! 6. Procure na documentação ou fale com um amiguinho para descrever
 o problema de domínio resolvido 7. Seja feliz!
  55. 55. OBRIGADO!
  56. 56. Referências •Foto dívida: https://flic.kr/p/oiBTYX •https://martinfowler.com/bliki/TechnicalDebt.html •https://martinfowler.com/bliki/ TechnicalDebtQuadrant.html •http://www.laputan.org/mud/ mud.html#ThrowAwayCode •http://www.laputan.org/mud/ •http://revista.ibict.br/ciinf/article/view/1058/1141

×