Slide 1

Slide 1 text

The Design of Everyday APIs Lynn Root @roguelynn Staff Engineer @ Spotify

Slide 2

Slide 2 text

$ whoami▮ $ whoami

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

– Don Norman, The Design of Everyday Things Design is concerned with how things work, how they are controlled, and the nature of the interaction between people and technology.

Slide 6

Slide 6 text

– Don Norman, The Design of Everyday Things Design is concerned with how things work, how they are controlled, and the nature of the interaction between people and technology.

Slide 7

Slide 7 text

– Don Norman, The Design of Everyday Things Two of the most important characteristics of good design are discoverability and understanding.

Slide 8

Slide 8 text

Discoverability: am I able to figure out what actions are possible, and where and how to perform them?

Slide 9

Slide 9 text

5 Key Elements of Discoverability

Slide 10

Slide 10 text

5 Key Elements of Discoverability ► Affordances

Slide 11

Slide 11 text

5 Key Elements of Discoverability ► Affordances ► Signi fi ers

Slide 12

Slide 12 text

5 Key Elements of Discoverability ► Affordances ► Signi fi ers ► Constraints

Slide 13

Slide 13 text

5 Key Elements of Discoverability ► Affordances ► Signi fi ers ► Constraints ► Mappings

Slide 14

Slide 14 text

5 Key Elements of Discoverability ► Affordances ► Signi fi ers ► Constraints ► Mappings ► Feedback

Slide 15

Slide 15 text

Understandability: Is it possible or easy for users to figure out how the product can be used?

Slide 16

Slide 16 text

Understanding System / Product

Slide 17

Slide 17 text

Understanding Conceptual Model System / Product User

Slide 18

Slide 18 text

Understanding Conceptual Model Conceptual Model System / Product Designer User

Slide 19

Slide 19 text

Understanding Conceptual Model Conceptual Model System / Product User Designer

Slide 20

Slide 20 text

Understanding User Designer Conceptual Model Conceptual Model System / Product

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

$ ffmpeg -filters wat wtf is all this no... plz stahp...

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

PLAY►

Slide 25

Slide 25 text

Good API Design

Slide 26

Slide 26 text

Good API Design LYNN’S 3 TENETS TO

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

1 of 2 class Message: def __init__(self, id: str, data: str, published_at: str) -> None: self.id = id self.data = data self.published_at = published_at Library

Slide 29

Slide 29 text

class Message: def __init__(self, id: str, data: str, published_at: str) -> None: class PubSubClient: def __init__(self, topic: str, subscription: str) -> None: def create_topic(self) -> None: def create_subscription(self) -> None: def add_message(self, msg: Message, timeout: int, retries: int) -> None: def get_message(self, timeout: int, retries: int) -> Message | None: def mark_message_done(self, msg_id: str, timeout: int, retries: int) -> None: def clear_message_queue(self) -> None: pass def close_client(self) -> None: pass Library 2 of 2

Slide 30

Slide 30 text

1 of 2 client = chaos_queue.PubSubClient(TOPIC, SUBSCRIPTION) try: client.create_topic() except chaos_queue.TopicExists: pass try: client.create_subscription() except chaos_queue.SubscriptionExists: pass User

Slide 31

Slide 31 text

2 of 2 for i in range(5): message = chaos_queue.Message( id=str(uuid.uuid4()), data=f"hello {i}", published_at=datetime.datetime.now().isoformat() ) try: client.add_message(message, 1, 0) except (chaos_queue.TimeoutError, queue.Full): pass while True: message = client.get_message(2, 2) if not message: break print(message) process_data(message.data) client.mark_message_done(message, 0, 0) client.close_client() User

Slide 32

Slide 32 text

for i in range(5): message = chaos_queue.Message( id=str(uuid.uuid4()), data=f"hello {i}", published_at=datetime.datetime.now().isoformat() ) try: client.add_message(message, 1, 0) except (chaos_queue.TimeoutError, queue.Full): pass while True: message = client.get_message(2, 2) if not message: break print(message) process_data(message.data) client.mark_message_done(message, 0, 0) client.close_client() User 2 of 2

Slide 33

Slide 33 text

for i in range(5): message = chaos_queue.Message( id=str(uuid.uuid4()), data=f"hello {i}", published_at=datetime.datetime.now().isoformat() ) try: client.add_message(message, 1, 0) except (chaos_queue.TimeoutError, queue.Full): pass while True: message = client.get_message(2, 2) if not message: break print(message) process_data(message.data) client.mark_message_done(message, 0, 0) client.close_client() User 2 of 2

Slide 34

Slide 34 text

for i in range(5): message = chaos_queue.Message( id=str(uuid.uuid4()), data=f"hello {i}", published_at=datetime.datetime.now().isoformat() ) try: client.add_message(message, 1, 0) except (chaos_queue.TimeoutError, queue.Full): pass while True: message = client.get_message(2, 2) if not message: break print(message) process_data(message.data) client.mark_message_done(message, 0, 0) client.close_client() User 2 of 2

Slide 35

Slide 35 text

intuitive intuitiv

Slide 36

Slide 36 text

Use Domain Nomenclature intuitive

Slide 37

Slide 37 text

before class PubSubClient: def __init__(self, topic: str, subscription: str) -> None: def create_topic(self) -> None: def create_subscription(self) -> None: def add_message(self, msg: Message, timeout: int, retries: int) -> None: def get_message(self, timeout: int, retries: int) -> Message | None: def mark_message_done(self, msg_id: str, timeout: int, retries: int) -> None: def clear_message_queue(self) -> None: pass def close_client(self) -> None: pass Library

Slide 38

Slide 38 text

before class PubSubClient: def __init__(self, topic: str, subscription: str) -> None: def create_topic(self) -> None: def create_subscription(self) -> None: def add_message(self, msg: Message, timeout: int, retries: int) -> None: def get_message(self, timeout: int, retries: int) -> Message | None: def mark_message_done(self, msg_id: str, timeout: int, retries: int) -> None: def clear_message_queue(self) -> None: pass def close_client(self) -> None: pass Library

Slide 39

Slide 39 text

after class PubSubClient: def __init__(self, topic: str, subscription: str) -> None: def create_topic(self) -> None: def create_subscription(self) -> None: def publish(self, msg: Message, timeout: int, retries: int) -> None: def pull(self, timeout: int, retries: int) -> Message | None: def ack(self, msg_id: str, timeout: int, retries: int) -> None: def drain(self) -> None: def close_client(self) -> None: Library

Slide 40

Slide 40 text

after class PubSubClient: def __init__(self, topic: str, subscription: str) -> None: def create_topic(self) -> None: def create_subscription(self) -> None: def publish(self, msg: Message, timeout: int, retries: int) -> None: def pull(self, timeout: int, retries: int) -> Message | None: def ack(self, msg_id: str, timeout: int, retries: int) -> None: def drain(self) -> None: def close_client(self) -> None: Library

Slide 41

Slide 41 text

Naming shouldn’t be hard intuitive

Slide 42

Slide 42 text

before Library class PubSubClient: def __init__(self, topic: str, subscription: str) -> None: def create_topic(self) -> None: def create_subscription(self) -> None: def publish(self, msg: Message, timeout: int, retries: int) -> None: def pull(self, timeout: int, retries: int) -> Message | None: def ack(self, msg_id: str, timeout: int, retries: int) -> None: def drain(self) -> None: def close_client(self) -> None:

Slide 43

Slide 43 text

before Library class PubSubClient: def __init__(self, topic: str, subscription: str) -> None: def create_topic(self) -> None: def create_subscription(self) -> None: def publish(self, msg: Message, timeout: int, retries: int) -> None: def pull(self, timeout: int, retries: int) -> Message | None: def ack(self, msg_id: str, timeout: int, retries: int) -> None: def drain(self) -> None: def close_client(self) -> None:

Slide 44

Slide 44 text

after class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def publish(self, msg: Message, timeout: int, retries: int) -> None: def close(self) -> None: class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def pull(self, timeout: int, retries: int) -> Message | None: def ack(self, msg_id: str, timeout: int, retries: int) -> None: def drain(self) -> None: def close(self) -> None: Library

Slide 45

Slide 45 text

Provide Symmetry intuitive

Slide 46

Slide 46 text

class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def publish(self, msg: Message, timeout: int, retries: int) -> None: def close(self) -> None: class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def pull(self, timeout: int, retries: int) -> Message | None: def ack(self, msg_id: str, timeout: int, retries: int) -> None: def drain(self) -> None: def close(self) -> None: before Library

Slide 47

Slide 47 text

class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def publish(self, msg: Message, timeout: int, retries: int) -> None: def close(self) -> None: class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def pull(self, timeout: int, retries: int) -> Message | None: def ack(self, msg_id: str, timeout: int, retries: int) -> None: def drain(self) -> None: def close(self) -> None: before Library

Slide 48

Slide 48 text

class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, msg: Message, timeout: int, retries: int) -> None: def close(self) -> None: class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, timeout: int, retries: int) -> Message | None: def ack(self, msg_id: str, timeout: int, retries: int) -> None: def drain(self) -> None: def close(self) -> None: after Library

Slide 49

Slide 49 text

class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, msg: Message, timeout: int, retries: int) -> None: def close(self) -> None: class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, timeout: int, retries: int) -> Message | None: def ack(self, msg_id: str, timeout: int, retries: int) -> None: def drain(self) -> None: def close(self) -> None: after Library

Slide 50

Slide 50 text

Use domain nomenclature Naming shouldn’t 
 be hard Provide symmetry Intuitive Tenet 2… Tenet 3…

Slide 51

Slide 51 text

flexible

Slide 52

Slide 52 text

Provide Sane Defaults flexible

Slide 53

Slide 53 text

class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, msg: Message, timeout: int, retries: int) -> None: def close(self) -> None: class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, timeout: int, retries: int) -> Message | None: def ack(self, msg_id: str, timeout: int, retries: int) -> None: def drain(self) -> None: def close(self) -> None: before Library

Slide 54

Slide 54 text

class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, msg: Message, timeout: int, retries: int) -> None: def close(self) -> None: class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, timeout: int, retries: int) -> Message | None: def ack(self, msg_id: str, timeout: int, retries: int) -> None: def drain(self) -> None: def close(self) -> None: before Library

Slide 55

Slide 55 text

after class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, msg: Message, timeout: int = 30, retries: int = 0, ) -> None: def close(self) -> None: class SubClient: ... Library

Slide 56

Slide 56 text

after Library class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, msg: Message, timeout: int = 30, retries: int = 0, ) -> None: def close(self) -> None: class SubClient: ...

Slide 57

Slide 57 text

after class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, msg: Message, timeout: int = 30, retries: int = 0, request_id: RequestIdType | bytes | str | None = None, debug: DebugLevelType | int | bool | None = None, ) -> None: def close(self) -> None: class SubClient: ... Library

Slide 58

Slide 58 text

class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, msg: Message, **kwargs) -> None: def close(self) -> None: class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, **kwargs) -> Message | None: def ack(self, msg_id: str, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: after Library Demo Only!

Slide 59

Slide 59 text

class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, msg: Message, **kwargs) -> None: def close(self) -> None: class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, **kwargs) -> Message | None: def ack(self, msg_id: str, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: after Library Demo Only! A void star-kwargs

Slide 60

Slide 60 text

Minimize Repetition flexible

Slide 61

Slide 61 text

class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, msg: Message, **kwargs) -> None: def close(self) -> None: before Library

Slide 62

Slide 62 text

class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, msg: Message, **kwargs) -> None: def close(self) -> None: before Library

Slide 63

Slide 63 text

class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, *msgs: Message, **kwargs) -> None: def close(self) -> None: after Library

Slide 64

Slide 64 text

class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, *msgs: Message, **kwargs) -> None: def close(self) -> None: after Library

Slide 65

Slide 65 text

class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, *msgs: Message, **kwargs) -> None: ... for message in msgs: self._queue.put(message, timeout=timeout) def close(self) -> None: after Library

Slide 66

Slide 66 text

Be Predictable and Precise flexible

Slide 67

Slide 67 text

class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, **kwargs) -> Message | None: def ack(self, msg_id: str, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: before Library

Slide 68

Slide 68 text

class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, **kwargs) -> Message | None: def ack(self, msg_id: str, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: before Library

Slide 69

Slide 69 text

class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, **kwargs) -> Message | None: def ack(self, msg_id: str, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: before Library

Slide 70

Slide 70 text

class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, **kwargs) -> Message: def ack(self, msg_id: str, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: after Library

Slide 71

Slide 71 text

class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, **kwargs) -> Message: ... try: return self._queue.get(timeout=timeout) except queue.Empty: raise ChaosEmptyError("Subscription queue is empty!") def ack(self, msg_id: str, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: after Library

Slide 72

Slide 72 text

Let Users Be Lazy flexible

Slide 73

Slide 73 text

class Message: def __init__(self, id: str, data: str, published_at: str) -> None: self.id = id self.data = data self.published_at = published_at before Library

Slide 74

Slide 74 text

OptStr = str | None class Message: def __init__( self, data: str, id: OptStr = None, published_at: OptStr = None ): self.data = data self.id = id or str(uuid.uuid4()) self.published_at = published_at or datetime.datetime.now().isoformat() after Library

Slide 75

Slide 75 text

OptStr = str | None class Message: def __init__( self, data: str, id: OptStr = None, published_at: OptStr = None ): self.data = data self.id = id or str(uuid.uuid4()) self.published_at = published_at or datetime.datetime.now().isoformat() def __repr__(self) -> str: return f"Message(id={self.id}, published_at={self.published_at})" after Library

Slide 76

Slide 76 text

from dataclasses import dataclass, field @dataclass class Message: data: str = field(repr=False) id: str = field(default=None) published_at: str = field(default=None) def __post_init__(self): self.id = self.id or str(uuid.uuid4()) self.published_at = self.published_at or datetime.datetime.now().isoformat option 2 after Library

Slide 77

Slide 77 text

from attrs import define, field @define class Message: data: str = field(repr=False) id: str = field() published_at: str = field() @id.default def set_id_default(self): return str(uuid.uuid4()) @published_at.default def set_published_at_default(self): return datetime.datetime.now().isoformat() after Library option 3

Slide 78

Slide 78 text

Use domain nomenclature Naming shouldn’t 
 be hard Provide symmetry Provide sane defaults Minimize repetition Be predictable and precise Let users be lazy Flexible Intuitive Tenet 3…

Slide 79

Slide 79 text

simple

Slide 80

Slide 80 text

Provide Composable Functions simple

Slide 81

Slide 81 text

class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, **kwargs) -> Message: def ack(self, msg_id: str, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: before Library

Slide 82

Slide 82 text

class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, **kwargs) -> Message: def ack(self, msg_id: str, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: before Library

Slide 83

Slide 83 text

class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, **kwargs) -> Message: def ack(self, msg_id: str, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: before Library

Slide 84

Slide 84 text

class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, **kwargs) -> Message: def ack(self, msg: Message, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: after Library

Slide 85

Slide 85 text

class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, **kwargs) -> Message: def ack(self, msg: Message, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: after Library

Slide 86

Slide 86 text

class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, **kwargs) -> Message: def ack(self, *msgs: Message, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: after Library

Slide 87

Slide 87 text

Leverage Language Idioms simple

Slide 88

Slide 88 text

class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, **kwargs) -> Message: def ack(self, *msgs: Message, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: before Library

Slide 89

Slide 89 text

class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, **kwargs) -> Message: def ack(self, *msgs: Message, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: before Library

Slide 90

Slide 90 text

class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, **kwargs) -> Message: def iter(self, **kwargs) -> Iterator[Message]: ... while not self._queue.empty(): message = self._queue.get(timeout=timeout) yield message def ack(self, *msgs: Message, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: after Library

Slide 91

Slide 91 text

class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, **kwargs) -> Message: def iter(self, **kwargs) -> Iterator[Message]: ... while not self._queue.empty(): message = self._queue.get(timeout=timeout) yield message raise StopIteration def ack(self, *msgs: Message, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: after Library

Slide 92

Slide 92 text

Leverage Language Idioms 
 (part 2) simple

Slide 93

Slide 93 text

before class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, *msgs: Message, **kwargs) -> None: def close(self) -> None: Library

Slide 94

Slide 94 text

before class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, *msgs: Message, **kwargs) -> None: def close(self) -> None: Library

Slide 95

Slide 95 text

class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, *msgs: Message, **kwargs) -> None: def close(self) -> None: def __enter__(self) -> Self: return self def __exit__(self, *args) -> None: self.close() after Library

Slide 96

Slide 96 text

class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, *msgs: Message, **kwargs) -> None: def close(self) -> None: def __enter__(self) -> Self: return self def __exit__(self, *args) -> None: self.close() after Library

Slide 97

Slide 97 text

simple Leverage Language Idioms (part 3)

Slide 98

Slide 98 text

class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, *msgs: Message, **kwargs) -> None: def close(self) -> None: def __enter__(self) -> Self: return self def __exit__(self, *args) -> None: self.close() Library before

Slide 99

Slide 99 text

class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, *msgs: Message, **kwargs) -> None: def close(self) -> None: def __enter__(self) -> Self: return self def __exit__(self, *args) -> None: self.close() Library before

Slide 100

Slide 100 text

class PubClient: def __init__(self, topic: str) -> None: def create(self, exist_ok: bool = False) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, *msgs: Message, **kwargs) -> None: def close(self) -> None: def __enter__(self) -> Self: return self def __exit__(self, *args) -> None: self.close() Library after Library

Slide 101

Slide 101 text

class PubClient: def __init__(self, topic: str) -> None: def create(self, exist_ok: bool = False) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, *msgs: Message, **kwargs) -> None: def close(self) -> None: def __enter__(self) -> Self: return self def __exit__(self, *args) -> None: self.close() Library after Library

Slide 102

Slide 102 text

Provide Convenience simple

Slide 103

Slide 103 text

README.md ## Installation $ pip install chaos-queue

Slide 104

Slide 104 text

## Installation $ pip install chaos-queue ## Get Started import chaos_queue messages = [chaos_queue.Message(str(i)) for i in range(5)] pub_client = chaos_queue.PubClient(TOPIC) with pub_client.create(): pub_client.publish(*messages, timeout=1, retry=False) README.md

Slide 105

Slide 105 text

## Installation $ pip install chaos-queue ## Get Started import chaos_queue messages = [chaos_queue.Message(str(i)) for i in range(5)] pub_client = chaos_queue.PubClient(TOPIC) with pub_client.create(): pub_client.publish(*messages, timeout=1, retry=False) ## Learn more Just go to chaos-queue.readthedocs.io for documentation and more examples! README.md

Slide 106

Slide 106 text

Use domain nomenclature Naming shouldn’t 
 be hard Provide symmetry Provide sane defaults Minimize repetition Be predictable and precise Let users be lazy Provide composable functions Leverage language idioms Provide convenience Flexible Intuitive Simple

Slide 107

Slide 107 text

◄◄ REWIND

Slide 108

Slide 108 text

before class Message: def __init__(self, id: str, data: str, published_at: str) -> None: self.id = id self.data = data self.published_at = published_at class PubSubClient: def __init__(self, topic: str, subscription: str) -> None: def create_topic(self) -> None: def create_subscription(self) -> None: def add_message(self, msg: Message, timeout: int, retries: int) -> None: def get_message(self, timeout: int, retries: int) -> Message | None: def mark_message_done(self, msg_id: str, timeout: int, retries: int) -> None: def clear_message_queue(self) -> None: pass def close_client(self) -> None: pass Library

Slide 109

Slide 109 text

after @define class Message: data: str = field(repr=False) id: str = field() published_at: str = field() @id.default def set_id_default(self): return str(uuid.uuid4()) @published_at.default def set_published_at_default(self): return datetime.datetime.now().isoformat() class PubClient: def __init__(self, topic: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def publish(self, *msgs: Message, **kwargs) -> None: def close(self) -> None: def __enter__(self) -> Self: def __exit__(self, *args) -> None: class SubClient: def __init__(self, subscription: str) -> None: def create(self) -> None: def delete(self) -> None: def update(self, config: dict) -> None: def pull(self, **kwargs) -> Message: def iter(self, **kwargs) -> Iterable[Message]: def ack(self, *msgs: Message, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: def __enter__(self) -> Self: def __exit__(self, *args) -> None: Library

Slide 110

Slide 110 text

before client = chaos_queue.PubSubClient(TOPIC, SUBSCRIPTION) try: client.create_topic() except chaos_queue.TopicExists: pass try: client.create_subscription() except chaos_queue.SubscriptionExists: pass for i in range(5): message = chaos_queue.Message( id=str(uuid.uuid4()), data=f"hello {i}", published_at=datetime.datetime.now().isoformat() ) try: client.add_message(message, 1, False) except (chaos_queue.TimeoutError, queue.Full): pass while True: message = client.get_message(2, True) if not message: break print(message) process_data(message.data) client.mark_message_done(message, None, False) client.close_client() User

Slide 111

Slide 111 text

after messages = [chaos_queue.Message(data=f"hello {i}") for i in range(5)] with chaos_queue.PubClient(TOPIC) as pub_client: pub_client.create(exist_ok=True) try: pub_client.publish(messages, timeout=1) except chaos_queue.TimeoutError: pass with chaos_queue.SubClient(SUBSCRIPTION) as sub_client: sub_client.create(exist_ok=True) for message in sub_client.iter(): print(message) process_data(message.data) sub_client.ack(message) User

Slide 112

Slide 112 text

Use domain nomenclature Naming shouldn’t 
 be hard Provide symmetry Provide sane defaults Minimize repetition Be predictable and precise Let users be lazy Provide composable functions Leverage language idioms Provide convenience Flexible Intuitive Simple

Slide 113

Slide 113 text

– Don Norman, The Design of Everyday Things If all else fails, standardize.

Slide 114

Slide 114 text

rogue.ly/apis Thank you! Lynn Root @roguelynn Slides, notes, & credits: