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.

Como fazer boas libs

Não é fácil escrever uma biblioteca confortável de usar. É difícil agradar a todos. Mas também é difícil agradar até quem acha que a nossa biblioteca faz algo útil. Não é fácil nem mesmo quando fazemos algo reusável só para nós mesmos.

Ainda bem que muitos outros programadores já erraram antes de nós. E existem em Python e fora dele diversos exemplos de boas libs nas quais podemos nos inspirar.

Essa palestra resumirá o que torna uma biblioteca boa, de acordo com nossa experiência e com a experiência de outros programadores que já escreveram sobre isso. Algumas características de boas bibliotecas são:

- Alta Consistência
- Muitos dados puros
- Baixa Verbosidade
- Respeito ao Principle of Least Astonishment
- Alta Extensibilidade
- Baixa Retenção
- Vários níveis de abstrações
- Alta granularidade
- Interesses claros e separados
- Pythonica

Como você pode ver, alguns aspectos acima são similares ao Zen of Python. Por isso também mostraremos funcionalidades do Python que ajudam a programar boas interfaces. Além disso, para resumir tudo definiremos um checklist que você poderá usar sempre que for escrever um módulo reusável. Esperamos que isso ajude você a programar melhor, o que certamente agradará seus parceiros de trabalho e a comunidade.

  • Login to see the comments

Como fazer boas libs

  1. 1. Como fazer boas libs? Flávio Juvenal Vinta Software www.vinta.com.br
  2. 2. ● Dia 16: Estrutura de dados e collections em Python - André Ericson (Sala Sambaqui 3) ● Dia 17: Definindo um Boilerplate Customizável usando Django, React e Bootstrap - Lais Varejão (Sala Sambaqui 3) Outras talks da Vinta na PyBR 12
  3. 3. Como fazer boas libs APIs?
  4. 4. Não vamos abordar ● Estrutura de projeto ● Distribuição ● Licença de uso ● Semantic Versioning ● Code-style ● Continuous Integration ● Coverage ● etc...
  5. 5. API "Um conjunto de definições de subrotinas, protocolos e ferramentas para construir software e aplicações." https://en.wikipedia.org/wiki/Application_programming_inter face
  6. 6. Sua API é uma UI
  7. 7. Sua API é uma UI 10 Usability Heuristics for User Interface Design https://www.nngroup.com/articles/ten-usability-heuristics/
  8. 8. Sua API é uma UI ● Visibility of system status ● Match between system and the real world ● User control and freedom ● Consistency and standards ● Recognition rather than recall ● Flexibility and efficiency of use ● Aesthetic and minimalist design ● Error prevention ● Help users recognize, diagnose, and recover from errors ● Help and documentation
  9. 9. Sua API é uma UI Hmm… bonito… mas abstrato demais... Máximas como esta são difíceis de aplicar e avaliar. Precisamos de princípios mais práticos!
  10. 10. Consistência
  11. 11. - Com ou sem underscore? gettype VS get_class (PHP) - objeto + verbo ou verbo + objeto? str_shuffle VS recode_string (PHP) Consistência na prática: nomenclatura
  12. 12. - Abreviações activatePreviousWindows VS fetchPrev (Qt < 4) bin2hex VS strtolower (PHP) Consistência na prática: nomenclatura
  13. 13. - Ordem dos argumentos? datetime.datetime(year, month, day, hour=0, minute=0, second=0, microsecond=0, tzinfo=None) datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0) Consistência na prática: ordem
  14. 14. - Significado de vazio: None, False, [], '', 0 - Use o mais adequado. Cuidado com significados obscuros: >>> import datetime >>> bool(datetime.date(datetime.MINYEAR, 1, 1)) True >>> bool(datetime.time(0)) # Python < 3.5 False Consistência na prática: semântica
  15. 15. [ ] A API exige que o usuário volte constantemente para documentação para saber como faz algo? [ ] Existe consistência na nomenclatura (ordem dos termos, abreviações, plurais, etc)? [ ] Existe consistência na ordem dos argumentos? [ ] Existe consistência no que significa vazio? Aumentar a consistência
  16. 16. Verbosidade
  17. 17. Zen of Python Flat is better than nested. Sparse is better than dense. Readability counts.
  18. 18. [ ] A API exige que você copie e cole muito código para fazer algo comum? Se sim, faça funções mais gerais (mas mantenha as mais específicas para não perder composability). Exemplo: Reduzir a verbosidade
  19. 19. import urllib.request gh_url = 'https://api.github.com/user' password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm() password_mgr.add_password(None, gh_url, 'user', 'pswd') handler = urllib.request.HTTPBasicAuthHandler(password_mgr) opener = urllib.request.build_opener(handler) opener.open(gh_url) Reduzir a verbosidade
  20. 20. Reduzir a verbosidade import requests session = requests.Session() session.auth = ('user', 'pswd') session.get('https://api.github.com/user')
  21. 21. [ ] É possível usar um tipo padrão adequado ao invés de um novo tipo customizado da API? urllib.request.HTTPPasswordMgrWithDefaultRealm VS tuple Reduzir a verbosidade
  22. 22. [ ] A API tem valores defaults sãos? // Aff... history.pushState(null, "", "https://vinta.com.br"); // Por que não é? history.pushState("https://vinta.com.br"); Reduzir a verbosidade
  23. 23. Principle of Least Astonishment
  24. 24. Principle of Least Astonishment Previna que seu cliente da API fale WAT. http://programmers.stackexchange.com/questions/187457/ what-is-the-principle-of-least-astonishment
  25. 25. JavaScript < ES5 >>> parseInt('010') >>> 8 Se o valor vier de um input do usuário, lascou… https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects /parseInt#ECMAScript_5_removes_octal_interpretation Principle of Least Astonishment
  26. 26. Python >>> int('010') >>> 10 Principle of Least Astonishment
  27. 27. JavaScript >>> new Date(2016, 1, 1) Mon Feb 01 2016 00:00:00 GMT-0300 (BRT) Principle of Least Astonishment
  28. 28. Python >>> datetime.datetime(2016, 1, 1).isoformat() '2016-01-01T00:00:00' Principle of Least Astonishment
  29. 29. [ ] PLA funcional: O comportamento padrão faz o que o usuário realmente espera? Testar com usuários reais! Garantir o Principle of Least Astonishment
  30. 30. Em Python, "assimetria de comportamentos deve se refletir em assimetria de formas": numbers.sort() VS sorted(numbers) .append() VS + Evitar misturar retornos com side-effects: http://stackoverflow.com/questions/13062423/is-making-in-place-operations-ret urn-the-object-a-bad-idea Principle of Least Astonishment
  31. 31. Cuidado com falsa simetria: QStatusBar::message(text, msecs) (Qt 3) QStatusBar::setMessage(text, msecs) QStatusBar::showMessage(text, msecs) (Qt 4) Principle of Least Astonishment
  32. 32. [ ] PLA não-funcional: O comportamento padrão está de acordo com as expectativas de performance, efeitos colaterais, safety, segurança, etc? Garantir o Principle of Least Astonishment
  33. 33. Abstrações vazadas
  34. 34. "Abstração: uma simplificação de algo muito mais complicado que está acontecendo por debaixo dos panos" Joel Spolsky em http://www.joelonsoftware.com/articles/LeakyAbstractions.h tml Abstrações
  35. 35. from collections import Counter c = Counter(['eggs', 'ham', 'eggs']) print(c) >>> Counter({'eggs': 2, 'ham': 1}) Abstrações
  36. 36. sorted([2,3,1]) >>> [1, 2, 3] Abstrações
  37. 37. PS: Mas só porque está acontecendo debaixo dos panos, não deve ser completamente invisível. O como às vezes precisa estar evidente. Mais sobre isso a seguir... Law of Leaky Abstractions
  38. 38. O problema é que "Todas abstrações não-triviais são vazadas". Esta é a "The Law of Leaky Abstractions". Ou seja, detalhes da implementação vazam. Law of Leaky Abstractions
  39. 39. O que causa vazamentos? Quando tentamos garantir algo impossível ou não-natural. Law of Leaky Abstractions
  40. 40. APIs RPC que tentam igualar chamadas remotas a chamadas locais. guido_profile = Facebook.get_profile('guido') guido_profile.set_name("Benevolent Dictator For Life") Law of Leaky Abstractions
  41. 41. REST não abstrai a dualidade cliente-servidor. Pelo contrário, abraça ela. Por exemplo com o Conditional PUT: Law of Leaky Abstractions
  42. 42. [ ] Existe algo que a API está tentando abstrair e não devia? Minimizar vazamentos das abstrações em APIs
  43. 43. Completude
  44. 44. Outro problema das abstrações é quando elas não são completas, são restritas. O cliente precisará estender a abstração com mais funcionalidades, mudar partes de funcionalidades existentes ou compor a abstração com outras. Completude e extensibilidade
  45. 45. Completude e extensibilidade "O maior objetivo de uma API deve ser eliminar a descontinuidade de integração." Casey Muratori em: https://mollyrocket.com/casey/stream_0028.html
  46. 46. ORM count ORM annotate com F object ORM com annotate e custom Aggregate ORM annotate com F object e Func
  47. 47. O cliente precisará estender a abstração com mais funcionalidades, mudar partes de funcionalidades existentes ou compor a abstração com outras. Não tem problema a API não ser completa, mas ela deve ser extensível, flexível e "composable". Completude e extensibilidade
  48. 48. Retenção
  49. 49. Um dos vilões da extensibilidade se chama retenção. Retenção ocorre quando a API sequestra ou encapsula um recurso e impede seu uso direto. Retenção
  50. 50. - retenção para ser + extensível [ ] Ofereça a opção de deixar um recurso interno aberto/alocado mesmo depois de ter terminado de usá-lo. [ ] Dê acesso (mesmo que com _) a recursos que o cliente pode precisar.
  51. 51. [ ] Retorne objetos que o cliente pode precisar. [ ] Ao fazer Inversion of Control ou chamar callbacks, passe tudo que a função do cliente pode precisar. Exemplo a seguir: - retenção para ser + extensível
  52. 52. Encapsulamento, sim ou não?
  53. 53. Além de mais retenção, encapsulamento total resulta em menos flexibilidade. Quem quiser mudar o comportamento interno vai conseguir de um jeito ou de outro (monkey-patching, reflection, etc)… então pra que esconder tanto? "For a Pythonista, encapsulation is not the inability of seeing internals of classes, but the possibility of avoiding to look at it" http://stackoverflow.com/questions/7456807/python-name-mangl ing Encapsulamento total para que?
  54. 54. Encapsulamento total para que?
  55. 55. - encapsulamento para ser + extensível [ ] A API permite a modificação ou remoção de comportamentos sem que o usuário tenha que reescrever o código interno dela? Permita sobrescrita para evitar reescrita! Exemplo dropzone.js:
  56. 56. - encapsulamento para ter + composability [ ] Aceite recursos oferecidos pelo cliente caso eles sejam similares aos seus (aproveite duck-typing). [ ] Ofereça a opção de aceitar um recurso externo previamente aberto/alocado.
  57. 57. [ ] Se for necessário fazer muitos mock.patch, então está faltando Inversion of Control/Dependecy Injection para ser mais flexível. Faça mais DI. Exemplo: - encapsulamento para ter + composability
  58. 58. - encapsulamento para ter + composability @patch('my.project.trading.get_enriched_trade_data', lambda: TEST_TRADE_DATA) @patch('my.project.subledger.get_subledger_data', get_test_subledger_data) @patch('my.project.metadata.get_subledger_codes', return_value=TEST_CODES) @patch('my.project.reports.store_report') def test_build_report(self, store_report): build_report() store_report.assert_called_with(EXPECTED_REPORT) http://mauveweb.co.uk/posts/2014/09/every-mock-patch-is-a-little-smell.html
  59. 59. Mais
  60. 60. níveis
  61. 61. de abstração
  62. 62. Se os três pontos de integração forem da mesma lib, já muito!
  63. 63. Mas o ideal é sempre mais
  64. 64. + níveis de abstração para ter + composability [ ] A API provê vários níveis de abstração, do mais abstrato ao mais específico/customizável/ baixo-nível? [ ] Na API, você deixou "o simples fácil, o complexo possível e o errado impossível"? Exemplo a seguir:
  65. 65. + níveis de abstração para ter + composability @app.task def add(x, y): return x + y
  66. 66. class AdderTask(Task): rate_limit = '100/h' def run(self, x, y): return x + y + níveis de abstração para ter + composability
  67. 67. Granular!
  68. 68. Não-granular!
  69. 69. [ ] Tem "and" no nome da função? A função muda o estado de vários objetos? A função tem mais de um interesse (concern)? Se sim, quebre a função maior fazendo ela chamar funções menores. Exemplo a seguir: + granularidade para ter + composability e + extensibilidade
  70. 70. ● Não forçar o usuário a especificar o que é óbvio ou frequente. ● Mas também não esconder o que é importante e muitos podem querer mudar. ● Quanto mais níveis de abstração a API tiver, mais a API vai acertar os níveis que os clientes precisam. Acertar o nível de abstração
  71. 71. Pythonic
  72. 72. ● Prefira @property a métodos import urllib.request r = urllib.request.urlopen('http://python.org/') r.getcode() import requests r = requests.get('http://python.org/') r.status_code Pythonic
  73. 73. ● Use Context managers para objetos com life-cycle f = open('file.txt') contents = f.read() f.close() with open('file.txt') as f: contents = f.read() Pythonic
  74. 74. ● Defina __repr__ >>> requests.get('http://httpbin.org/status/200') <Response [200]> >>> Github().gists() <TapiocaClientExecutor object 'https://api.github.com/gists'> Pythonic
  75. 75. Pythonic ● Operator overloading ● Decorators ● Generators ● asyncio ● etc... "Design Patterns are missing language features" Exemplo: strategy pattern < high order functions
  76. 76. ● Documentação!!!!!!!!!! ○ Mostrar quick-start com poucas linhas para fazer o básico ○ Top-down: primeiro pensar em o que a API deve fazer, documentar isso, e só depois implementar ○ Exemplos hands-on, com casos de uso (exemplo: python-social-auth) ○ Documentar erros comuns, depois proteger a API contra eles (se estiver falando muito "note" ou "careful", mude sua API) Também é importante
  77. 77. ● Visibilidade de contexto: cuidado com interfaces modais ● Exceções ○ Vários tipos ○ Mensagens de erro ○ Fail-fast, especialmente em operações sequenciais ○ "Errors should never pass silently. Unless explicitly silenced." ● Safety (não é segurança!) ● Imutabilidade ● Discoverability ● etc... Também é importante
  78. 78. ● Slides disponíveis em: http://bit.ly/pybr12-vinta ● Twitter: @flaviojuvenal Dúvidas?
  79. 79. ● Designing and Evaluating Reusable Components ● The Little Manual of API Design ● API Design Tips ● API Design ● What Makes Software Good? ● What makes a good software library? ● Bumper-Sticker API Design ● Ten Good Rules for Bad APIs Referências
  80. 80. Referências ● What guidelines should I follow while designing a library? ● API: Design Matters ● How to Design a Good API and Why it Matters ● API Design Principles - Qt Wiki ● PHP: a fractal of bad design ● Why frameworks are evil ● Multiple levels of abstraction ● The Law of Leaky Abstractions
  81. 81. Referências ● On DRY and the cost of wrongful abstractions ● The Wrong Abstraction ● Separation of concerns ● Command–query separation ● PEP 8

×