Slide 1

Slide 1 text

Because you’re a nice person Library UX Using abstraction towards friendlier APIs Mali Akmanalp (@makmanalp) hear at the back Who here has had to write code that other people have to consume?

Slide 2

Slide 2 text

bit.ly/abstraction-talk (@makmanalp) presenter notes links tidbits

Slide 3

Slide 3 text

UX == User Experience == How this makes me feel How do I feel when I use this thing? excited / frustrated product companies think about UX - e.g. apple products

Slide 4

Slide 4 text

UX == User Experience == Usability overlapping How easy is it to learn and use this thing? — both come late into consideration, if ever talk by kenneth reitz

Slide 5

Slide 5 text

import urllib2 gh_url = 'https://api.github.com/user' 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) print handler.read() https://www.kennethreitz.org/python-for-humans/ github API HTTP request custom class

Slide 6

Slide 6 text

import requests url = 'https://api.github.com/user' auth = ('username', 'password') r = requests.get(url, auth=auth) print r.content https://www.kennethreitz.org/python-for-humans/ Drastically different experience “urllib is so terrible” || mad at me Don't jump to conclusions / Hold that thought - Alternate theory

Slide 7

Slide 7 text

"For Humans" code is for people people have feelings

Slide 8

Slide 8 text

Why care about UX? Why care about how users feel? (nice person) many reasons

Slide 9

Slide 9 text

Good UX reduces mistakes. n

Slide 10

Slide 10 text

import urllib2 gh_url = 'https://api.github.com/user' 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) print handler.read() What is each parameter? noun_verb or verbnoun? reduces mistakes https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4173163/

Slide 11

Slide 11 text

import urllib2 gh_url = 'https://api.github.com/user' 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) print handler.read() What is each parameter? noun_verb or verbnoun? reduces mistakes https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4173163/

Slide 12

Slide 12 text

import urllib2 gh_url = 'https://api.github.com/user' 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) print handler.read() What is each parameter? noun_verb or verbnoun? reduces mistakes https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4173163/

Slide 13

Slide 13 text

import urllib2 gh_url = 'https://api.github.com/user' 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) print handler.read() What is each parameter? noun_verb or verbnoun? reduces mistakes https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4173163/

Slide 14

Slide 14 text

Good UX minimizes distractions. I don’t care about your lib's impl details it's not about you - it's about me Your library is a means to my end. Just let me do my job!

Slide 15

Slide 15 text

Good UX makes complex tasks routine. “batteries included” feeling of tools in my toolbox boilerplate song and dance / incantations Requests + beautifulsoup + pandas + seaborn

Slide 16

Slide 16 text

Good UX drives adoption. n

Slide 17

Slide 17 text

(sometimes Built-in) alternatives

Slide 18

Slide 18 text

? how do we get better UX? switch gears / seemingly unrelated

Slide 19

Slide 19 text

http://abstrusegoose.com/307 abstraction analyze the joke to death stack of pancakes asteroids->empty void of space Abstraction works even if the architects don't know the very blocks they're building upon

Slide 20

Slide 20 text

Claim: We are primarily in the business of dealing with abstractions. we would do well to pay more attention to them

Slide 21

Slide 21 text

http://abstrusegoose.com/307 You are here Python: fall in the middle theory Py aweosmeness: HL enough -> goal-driven needs C n - but what is abs really?

Slide 22

Slide 22 text

Abstraction is about hiding details in a controlled way. (pause) why hide?

Slide 23

Slide 23 text

Hiding details helps reduce mistakes. n

Slide 24

Slide 24 text

import requests url = 'https://api.github.com/user' auth = ('username', 'password') r = requests.get(url, auth=auth) print r.content https://www.kennethreitz.org/python-for-humans/ requests is highly abstracted less code == less errors

Slide 25

Slide 25 text

Hiding details makes complex tasks routine. Cool thing: I don’t need to know maxwell’s eqs. to make a game helps reason about complex ideas

Slide 26

Slide 26 text

https://xkcd.com/378/ make fun of “real programmer” Butterfly based disk I/O most of your time is spent thinking about butterflies

Slide 27

Slide 27 text

Hiding details provides a stable interface. changes under the hood zero-cost for your users testing is easier!

Slide 28

Slide 28 text

Claim: Good abstraction is aligned with good UX. mentioned things as "Good UX"

Slide 29

Slide 29 text

ABSTRACTION IN PYTHON what tools do we have?

Slide 30

Slide 30 text

Functions >>> a array([[ 2., 8., 0., 6.], [ 4., 5., 1., 1.], [ 8., 9., 3., 6.]]) >>> np.resize(a, (2,6)) array([[ 2., 8., 0., 6., 4., 5.], [ 1., 1., 8., 9., 3., 6.]]) functions abstract away the details edge cases blasphemies: speed

Slide 31

Slide 31 text

Classes class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(50)) dessert = Column(String(50)) another tool sqlalchemy models magic

Slide 32

Slide 32 text

Classes mali = User(name="mali", fullname="Mali Akmanalp", password="pumpkin")

Slide 33

Slide 33 text

Classes >>> mali.name "mali" >>> mali.name = "mali2" >>> session.add(mali) >>> session.commit() classes make state explicit and organized group state and behavior - clean behavior that changes with state

Slide 34

Slide 34 text

PITFALLS With great power comes great responsibility. n

Slide 35

Slide 35 text

Leaky abstractions when the details refuse to be hidden http://www2.parc.com/csl/groups/sda/publications/papers/Kiczales-IMSA92/for-web.pdf

Slide 36

Slide 36 text

Leaky Abstractions size = 1000 big_table = [list(range(size)) for _ in range(size)] 1000x1000

Slide 37

Slide 37 text

Leaky Abstractions [[ 2, 8, …, 0, 6], [ 4, 5, …, 1, 1], [ …, …, …, …, …], [ …, …, …, …, …], [ 8, 2, …, 5, 6], [ 8, 9, …, 3, 6]]) n

Slide 38

Slide 38 text

Leaky Abstractions In [5]: %%timeit ...: for i in range(size): ...: for j in range(size): ...: x = big_table[j][i] ...: 10 loops, best of 3: 163 ms per loop

Slide 39

Slide 39 text

Leaky Abstractions In [5]: %%timeit ...: for i in range(size): ...: for j in range(size): ...: x = big_table[i][j] ...: 10 loops, best of 3: 97.1 ms per loop ???

Slide 40

Slide 40 text

Leaky Abstractions In [5]: %%timeit ...: for i in range(size): ...: for j in range(size): ...: x = big_table[i][j] ...: 10 loops, best of 3: 97.1 ms per loop ???

Slide 41

Slide 41 text

Leaky Abstractions In [5]: %%timeit ...: for i in range(size): ...: for j in range(size): ...: x = big_table[i][j] ...: 10 loops, best of 3: 97.1 ms per loop ???

Slide 42

Slide 42 text

Leaky Abstractions [[ 2, 8, …, 0, 6], [ 4, 5, …, 1, 1], [ …, …, …, …, …], [ …, …, …, …, …], [ 8, 2, …, 5, 6], [ 8, 9, …, 3, 6]]) against the grain "law of leaky abstractions" acceptable compromise

Slide 43

Slide 43 text

Leaky Abstractions [[ 2, 8, …, 0, 6], [ 4, 5, …, 1, 1], [ …, …, …, …, …], [ …, …, …, …, …], [ 8, 2, …, 5, 6], [ 8, 9, …, 3, 6]]) against the grain "law of leaky abstractions" acceptable compromise

Slide 44

Slide 44 text

Leaky Abstractions [[ 2, 8, …, 0, 6], [ 4, 5, …, 1, 1], [ …, …, …, …, …], [ …, …, …, …, …], [ 8, 2, …, 5, 6], [ 8, 9, …, 3, 6]]) against the grain "law of leaky abstractions" acceptable compromise

Slide 45

Slide 45 text

Leaky Abstractions [[ 2, 8, …, 0, 6], [ 4, 5, …, 1, 1], [ …, …, …, …, …], [ …, …, …, …, …], [ 8, 2, …, 5, 6], [ 8, 9, …, 3, 6]]) against the grain "law of leaky abstractions" acceptable compromise

Slide 46

Slide 46 text

Leaky Abstractions [[ 2, 8, …, 0, 6], [ 4, 5, …, 1, 1], [ …, …, …, …, …], [ …, …, …, …, …], [ 8, 2, …, 5, 6], [ 8, 9, …, 3, 6]]) against the grain "law of leaky abstractions" acceptable compromise

Slide 47

Slide 47 text

Leaky Abstractions [[ 2, 8, …, 0, 6], [ 4, 5, …, 1, 1], [ …, …, …, …, …], [ …, …, …, …, …], [ 8, 2, …, 5, 6], [ 8, 9, …, 3, 6]]) against the grain "law of leaky abstractions" acceptable compromise

Slide 48

Slide 48 text

Leaky Abstractions [[ 2, 8, …, 0, 6], [ 4, 5, …, 1, 1], [ …, …, …, …, …], [ …, …, …, …, …], [ 8, 2, …, 5, 6], [ 8, 9, …, 3, 6]]) against the grain "law of leaky abstractions" acceptable compromise

Slide 49

Slide 49 text

Leaky Abstractions [[ 2, 8, …, 0, 6], [ 4, 5, …, 1, 1], [ …, …, …, …, …], [ …, …, …, …, …], [ 8, 2, …, 5, 6], [ 8, 9, …, 3, 6]]) against the grain "law of leaky abstractions" acceptable compromise

Slide 50

Slide 50 text

Leaky Abstractions [[ 2, 8, …, 0, 6], [ 4, 5, …, 1, 1], [ …, …, …, …, …], [ …, …, …, …, …], [ 8, 2, …, 5, 6], [ 8, 9, …, 3, 6]]) against the grain "law of leaky abstractions" acceptable compromise

Slide 51

Slide 51 text

Leaky Abstractions [[ 2, 8, …, 0, 6], [ 4, 5, …, 1, 1], [ …, …, …, …, …], [ …, …, …, …, …], [ 8, 2, …, 5, 6], [ 8, 9, …, 3, 6]]) against the grain "law of leaky abstractions" acceptable compromise

Slide 52

Slide 52 text

Leaky Abstractions [[ 2, 8, …, 0, 6], [ 4, 5, …, 1, 1], [ …, …, …, …, …], [ …, …, …, …, …], [ 8, 2, …, 5, 6], [ 8, 9, …, 3, 6]]) against the grain "law of leaky abstractions" acceptable compromise

Slide 53

Slide 53 text

Leaky Abstractions [[ 2, 8, …, 0, 6], [ 4, 5, …, 1, 1], [ …, …, …, …, …], [ …, …, …, …, …], [ 8, 2, …, 5, 6], [ 8, 9, …, 3, 6]]) against the grain "law of leaky abstractions" acceptable compromise

Slide 54

Slide 54 text

Under-abstraction n

Slide 55

Slide 55 text

Guts everywhere State everywhere Control flow everywhere Hard to understand things Sign: People w/ domain knowledge have a hard time understanding

Slide 56

Slide 56 text

Over-abstraction watch out for the following

Slide 57

Slide 57 text

Coupling: To change one thing, you must change all things. n

Slide 58

Slide 58 text

Cohesion: A thing that does too many things at the same time. low-cohesion (contents not related to each other) n

Slide 59

Slide 59 text

DECIDING ON THE LEVEL OF ABSTRACTION so, you're building a shiny new app how to structure abstraction stack

Slide 60

Slide 60 text

http://abstrusegoose.com/307 back to this model simplify a little

Slide 61

Slide 61 text

one option

Slide 62

Slide 62 text

No content

Slide 63

Slide 63 text

No content

Slide 64

Slide 64 text

???

Slide 65

Slide 65 text

??? ???

Slide 66

Slide 66 text

??? ??? meaningful grouping

Slide 67

Slide 67 text

??? ??? ??? ??? remove layers w/ preserve grouping?

Slide 68

Slide 68 text

????? clearly a lot of decisions to make advice

Slide 69

Slide 69 text

Press release first PM saying: —— next —— "library allows developers to compose queries programmatically" "Tired of repetitive CRUD app code? No more!!!" "machine learning in 3 lines of code!" "Data munging? No Problem!" - tears of joy dask.delayed

Slide 70

Slide 70 text

Press release first PM saying: —— next —— "library allows developers to compose queries programmatically" "Tired of repetitive CRUD app code? No more!!!" "machine learning in 3 lines of code!" "Data munging? No Problem!" - tears of joy dask.delayed

Slide 71

Slide 71 text

Press release first PM saying: —— next —— "library allows developers to compose queries programmatically" "Tired of repetitive CRUD app code? No more!!!" "machine learning in 3 lines of code!" "Data munging? No Problem!" - tears of joy dask.delayed

Slide 72

Slide 72 text

Press release first PM saying: —— next —— "library allows developers to compose queries programmatically" "Tired of repetitive CRUD app code? No more!!!" "machine learning in 3 lines of code!" "Data munging? No Problem!" - tears of joy dask.delayed

Slide 73

Slide 73 text

Press release first PM saying: —— next —— "library allows developers to compose queries programmatically" "Tired of repetitive CRUD app code? No more!!!" "machine learning in 3 lines of code!" "Data munging? No Problem!" - tears of joy dask.delayed

Slide 74

Slide 74 text

"Imaginary Code" second play pretend write code using your imaginary library forces you to be specific ~~ contract-first development

Slide 75

Slide 75 text

Rewrite usage examples with existing libraries how have other people tackled similar problems? Is there an improvement?

Slide 76

Slide 76 text

What does it cost me? pitfalls: cohesion, coupling surface area potential cost of changing / un-doing this abstraction?

Slide 77

Slide 77 text

How likely is this to change? generalizations for events that won't happen Do you really need an n-dimensional chess-board class just to make a chess game?

Slide 78

Slide 78 text

How does this abstraction benefit the user? UX reduce cognitive load and complexity make code shorter?

Slide 79

Slide 79 text

Don't Repeat Yourself? explanation … feels good

Slide 80

Slide 80 text

Don't Refactor Yet! https://news.ycombinator.com/item?id=12528181 counterpoint adds layers (I didn't make this up)

Slide 81

Slide 81 text

–Sandi Metz "Prefer duplication over the wrong abstraction" https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction is it really that bad? sometimes yes worth thinking about

Slide 82

Slide 82 text

Incremental architecture Finally, given all these tips what's the hurry?

Slide 83

Slide 83 text

Good architecture and abstraction decisions follow from domain knowledge. n

Slide 84

Slide 84 text

More time on project More domain knowledge n ergo …

Slide 85

Slide 85 text

Earlier on in the project you have less domain knowledge n

Slide 86

Slide 86 text

Build less structure up front find out that you were off about what you're building don't fix problems before you have them add incrementally avoid "second system effect" Fred Brooks

Slide 87

Slide 87 text

TRICKS OF THE TRADE n

Slide 88

Slide 88 text

Trick: Abstraction need not mean building a wall. just a hood, not a wall

Slide 89

Slide 89 text

n

Slide 90

Slide 90 text

solid / see-through

Slide 91

Slide 91 text

Flask from flask import app @app.route('/dessert') def yum(): return "donuts!" explain! decompose Werkzeug under the hood

Slide 92

Slide 92 text

Flask from flask import app @app.route('/dessert') def yum(): return "donuts!" explain! decompose Werkzeug under the hood

Slide 93

Slide 93 text

Flask from flask import app def yum(): return "donuts!" app.add_url_rule('/', 'dessert', dessert)

Slide 94

Slide 94 text

Flask from flask import app def yum(): return "donuts!" app.add_url_rule('/', 'dessert', dessert)

Slide 95

Slide 95 text

Flask def add_url_rule(self, rule, **options, ...): # ... rule = self.url_rule_class(rule, **options, ...) # ... self.url_map.add(rule) # ... https://github.com/pallets/flask/blob/501f0431259a30569a5e62bcce68d102fc3ef993/flask/app.py#L1071 If I look at url_map - werkzeug Map object! Multiple access points “under the hood” - no wall requests / urllib3 obj

Slide 96

Slide 96 text

Flask def add_url_rule(self, rule, **options, ...): # ... rule = self.url_rule_class(rule, **options, ...) # ... self.url_map.add(rule) # ... https://github.com/pallets/flask/blob/501f0431259a30569a5e62bcce68d102fc3ef993/flask/app.py#L1071 If I look at url_map - werkzeug Map object! Multiple access points “under the hood” - no wall requests / urllib3 obj

Slide 97

Slide 97 text

Flask def add_url_rule(self, rule, **options, ...): # ... rule = self.url_rule_class(rule, **options, ...) # ... self.url_map.add(rule) # ... https://github.com/pallets/flask/blob/501f0431259a30569a5e62bcce68d102fc3ef993/flask/app.py#L1071 If I look at url_map - werkzeug Map object! Multiple access points “under the hood” - no wall requests / urllib3 obj

Slide 98

Slide 98 text

Trick: More layers can make things cleaner. not always true / great effect

Slide 99

Slide 99 text

which layers should the user know about?

Slide 100

Slide 100 text

traditionally

Slide 101

Slide 101 text

meaningful groupings potentially familiar?

Slide 102

Slide 102 text

expose each individually

Slide 103

Slide 103 text

Bokeh Charts Bokeh Glyphs Bokeh JS interactive online visualization create exact same chart in all 3 different levels of configurability and effort expose all 3!

Slide 104

Slide 104 text

SQLAlchemy ORM SQLAlchemy Core db toolkit

Slide 105

Slide 105 text

Seaborn Matplotlib different creators seaborn hi, MPL lo

Slide 106

Slide 106 text

CONCLUSION conclude with this thought - different authors chose different levels

Slide 107

Slide 107 text

Claim: The right level of abstraction is audience-specific. (pause)

Slide 108

Slide 108 text

Requests vs urllib2 import requests url = 'https://api.github.com/user' auth = ('username', 'password') r = requests.get(url, auth=auth) print r.content import urllib2 gh_url = 'https://api.github.com/ user' req = urllib2.Request(gh_url) password_manager = urllib2.HTTPPasswordMgrWithDefaultRea lm() 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) print handler.read() is Urllib2 under-abstracted? audience-specific urllib “wrong” level for you, right level for library devs

Slide 109

Slide 109 text

http://fishsnack.deviantart.com/art/Creation-of-Adam-in-the-21st-Century-280501206 Maybe it's ok! Some APIs are closer to the machine Some APIs are closer to the human We need both

Slide 110

Slide 110 text

Theory: “For Humans” adds a layer we didn’t know we were missing. (pause) we were sorely lacking it

Slide 111

Slide 111 text

Abstraction isn't a goal, It's a tool. can be used for good or bad it's everywhere it's a tool we inadvertently use hopefully you start seeing it everywhere / make UX better

Slide 112

Slide 112 text

Hearing from you makes me happy! (@makmanalp) slides on my twitter

Slide 113

Slide 113 text

No content

Slide 114

Slide 114 text

No content

Slide 115

Slide 115 text

No content

Slide 116

Slide 116 text

No content

Slide 117

Slide 117 text

Classes select * from users where name = "mali"; classes any user?

Slide 118

Slide 118 text

Classes """ select * from users where name = '{}'; """.format("mali") now we need to add another parameter

Slide 119

Slide 119 text

Classes """ select * from users where name = '{}' and dessert = '{}'; """.format("mali", "pie") ugh

Slide 120

Slide 120 text

Classes User.query\ .filter_by(name=“mali”)\ .all() SQLAlchemy

Slide 121

Slide 121 text

Classes User.query\ .filter_by( name=“mali”, dessert="cake")\ .all() .query returns a Query object. variables! arbitrary dictionary!

Slide 122

Slide 122 text

Classes class User(Base): __tablename__ = 'users' id = Column(Integer,primary_key=True) name = Column(String(50)) dessert = Column(String(50)) classes make state explicit and organized associate and group state and behavior

Slide 123

Slide 123 text

hiding details makes things easy to use??

Slide 124

Slide 124 text

Read a lot of code! Start with a library you use all the time Start with a function you know Find the code and read! Ask for help!

Slide 125

Slide 125 text

Hiding details papers over grossness??? Cory B speaking about the guts of requests https://us.pycon.org/2017/schedule/presentation/71/

Slide 126

Slide 126 text

http://aminotes.tumblr.com/post/3686652856/the-process-of-abstracting-according-to-s-i

Slide 127

Slide 127 text

http://www.rijnlandmodel.nl/english/general_semantics/abstraction_ladder.htm Hayakawa

Slide 128

Slide 128 text

Y PITFALLS Z n

Slide 129

Slide 129 text

https://xkcd.com/378/ make fun of “real programmer” Butterfly vs Magnet needle vs File Systems Files aren’t “real” it’s all zeroes and ones! to real apps