Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Simplicity is a Feature

Cory Benfield
September 20, 2015

Simplicity is a Feature

When you talk to software professionals, you come away with the idea that we have a case of feature-itis. An enormous number of people evaluate software in terms of the length of feature lists and functionality. People dismiss software with fewer features as “underpowered”. And how many times have you heard software devalued because “I could build that”?

And yet, intuitively we know that’s not always true. urllib3 has more features than requests, but people overwhelmingly prefer to use requests. Android has more features than iOS, but Android hasn’t demolished iOS.

The reason for this is that simplicity is a powerful advantage. This talk will explain what is meant by simplicity, how to judge software simplicity, and how to approach building simple tools. It’ll also talk about how to structure your code to give the optimum balance of simplicity and flexibility, to keep yourself and your users happy down the line.

Cory Benfield

September 20, 2015
Tweet

More Decks by Cory Benfield

Other Decks in Programming

Transcript

  1. Simplicity
    Is A Feature

    View Slide

  2. Hi!

    View Slide

  3. Me
    @lukasaoz
    @lukasa

    View Slide

  4. View Slide

  5. Warning
    Opinions!

    View Slide

  6. What Is
    Simplicity?

    View Slide

  7. View Slide

  8. View Slide

  9. View Slide

  10. View Slide

  11. import requests
    r = requests.get(
    ‘https://api.github.com',
    auth=('user', ‘pass')
    )

    View Slide

  12. import urllib2
    gh_url = 'https://api.github.com'
    req = urllib2.Request(gh_url)
    password_manager =
    urllib2.HTTPPasswordMgrWithDefaultRealm()
    password_manager.add_password(None, gh_url, 'user',
    'pass')
    auth_manager =
    urllib2.HTTPBasicAuthHandler(password_manager)
    opener = urllib2.build_opener(auth_manager)
    urllib2.install_opener(opener)
    handler = urllib2.urlopen(req)

    View Slide

  13. – Justice Potter Stewart
    “I know it when I
    see it.”

    View Slide

  14. Simplicity

    Features

    View Slide

  15. Simplicity is
    about defaults

    View Slide

  16. Why Be
    Simple?

    View Slide

  17. – Edsger Dijkstra
    “Simplicity is a
    prerequisite for
    reliability.”

    View Slide

  18. – Robert Browning
    “Less is more.”

    View Slide

  19. – Henry David Thoreau
    “Our life is
    frittered away by
    detail. Simplify.”

    View Slide

  20. How To Be
    Simple

    View Slide

  21. View Slide

  22. How To
    Onion

    View Slide

  23. Build In
    Layers

    View Slide

  24. Layer 1: Low-level

    View Slide

  25. import email.generator
    import http.client
    import io
    import os
    import shutil
    def _encode_multipart_formdata(fields, files):
    boundary = email.generator._make_boundary()
    buf = io.BytesIO()
    textwriter = io.TextIOWrapper(
    buf, 'utf8', newline='', write_through=True)
    for (key, value) in fields.items():
    textwriter.write(
    '--{boundary}\r\n'
    'Content-Disposition: form-data; name="{key}"\r\n\r\n'
    '{value}\r\n'.format(
    boundary=boundary, key=key, value=value))
    for (key, filepath, filename) in files:
    if os.path.isfile(filepath):
    textwriter.write(
    '--{boundary}\r\n'
    'Content-Disposition: form-data; name="{key}"; '
    'filename="{filename}"\r\n'
    'Content-Type: {content_type}\r\n\r\n'.format(
    boundary=boundary, key=key, filename=filename,
    content_type='application/octet-stream'))
    with open(filepath, "rb") as f:
    shutil.copyfileobj(f, buf)
    textwriter.write('\r\n')
    textwriter.write('--{}--\r\n\r\n'.format(boundary))
    content_type = 'multipart/form-data; boundary={}'.format(boundary)
    return content_type, buf.getvalue()
    content_type, data = _encode_multipart_formdata(
    fields={'key': 'value'},
    files=[('filekey', 'test.py', 'test.py')]
    )
    headers = {
    'Accept-Encoding': 'gzip, deflate',
    'Accept': '*/*',
    'Connection': 'keep-alive',
    'User-Agent': 'python-requests/2.7.0 CPython/2.7.10 Darwin 14.5.0',
    'Content-Type': content_type,
    'Content-Length': '{}'.format(len(data)),
    }
    c = http.client.HTTPSConnection('http2bin.org', 443)
    c.request('POST', '/post', body=data, headers=headers)
    resp = c.getresponse()
    print(resp.read())

    View Slide

  26. Layer 1: Low-level
    Layer 2: Features

    View Slide

  27. import urllib3
    from urllib3.filepost import RequestField
    fields = [
    RequestField(
    'key',
    'value',
    ),
    RequestField(
    'filekey',
    open('test.py').read(),
    filename='test.py',
    ),
    ]
    fields[0].make_multipart()
    fields[1].make_multipart(content_type='application/octet-stream')
    headers = {'User-Agent': 'python-requests/2.7.0 CPython 2.7.10 Darwin/14.5.0'}
    http = urllib3.PoolManager()
    r = http.request('POST', 'http://http2bin.org/post', fields=fields)
    print(r.data)

    View Slide

  28. Layer 1: Low-level
    Layer 2: Features
    Layer 3: Simplicity

    View Slide

  29. import requests
    r = requests.post(
    'https://http2bin.org/post',
    files={
    'key': 'value',
    'filekey': (
    'test.py',
    open('test.py'),
    'application/octet-stream'
    )
    }
    )
    print(r.text)

    View Slide

  30. While We’re
    Here…

    View Slide

  31. 80%

    View Slide

  32. View Slide

  33. Thanks!
    ✨✨

    View Slide