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

5 Key Elements of Discoverability

Slide 9

Slide 9 text

5 Key Elements of Discoverability ► Affordances

Slide 10

Slide 10 text

5 Key Elements of Discoverability ► Affordances ► Signi fi ers

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

Understanding

Slide 15

Slide 15 text

Understanding System / Product

Slide 16

Slide 16 text

Understanding Conceptual Model System / Product User

Slide 17

Slide 17 text

Understanding Conceptual Model Conceptual Model System / Product Designer User

Slide 18

Slide 18 text

Understanding Conceptual Model Conceptual Model System / Product User Designer

Slide 19

Slide 19 text

Understanding User Designer Conceptual Model Conceptual Model System / Product

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

PLAY►

Slide 24

Slide 24 text

Good API Design

Slide 25

Slide 25 text

Good API Design LYNN’S 3 TENETS TO

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 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 28

Slide 28 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 29

Slide 29 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 30

Slide 30 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 31

Slide 31 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 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

intuitive intuitiv

Slide 35

Slide 35 text

Use Domain Nomenclature intuitive

Slide 36

Slide 36 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 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

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 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

Clumsy Naming Hints at Clumsy Abstractions intuitive

Slide 41

Slide 41 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 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

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 44

Slide 44 text

Provide Symmetry intuitive

Slide 45

Slide 45 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 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 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 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

Use domain nomenclature Clumsy naming hints at clumsy abstractions Provide symmetry Intuitive Tenet 2… Tenet 3…

Slide 50

Slide 50 text

flexible

Slide 51

Slide 51 text

Provide Sane Defaults flexible

Slide 52

Slide 52 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 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

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 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, request_id: RequestIdType | bytes | str | None = None, debug: DebugLevelType | int | bool | None = None, ) -> None: def close(self) -> None: class SubClient: ... Library

Slide 56

Slide 56 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

Slide 57

Slide 57 text

Minimize Repetition flexible

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: before Library

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: before Library

Slide 60

Slide 60 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 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, *msgs: Message, **kwargs) -> None: def close(self) -> None: after 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, *msgs: Message, **kwargs) -> None: ... for message in msgs: self._queue.put(message, timeout=timeout) def close(self) -> None: after Library

Slide 63

Slide 63 text

Be Predictable and Precise flexible

Slide 64

Slide 64 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 65

Slide 65 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 66

Slide 66 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 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: def ack(self, msg_id: str, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: after 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: ... 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 69

Slide 69 text

Let Users Be Lazy flexible

Slide 70

Slide 70 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 71

Slide 71 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 72

Slide 72 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 73

Slide 73 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 74

Slide 74 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 75

Slide 75 text

Use domain nomenclature Clumsy naming hints at clumsy abstractions Provide symmetry Provide sane defaults Minimize repetition Be predictable and precise Let users be lazy Flexible Intuitive Tenet 3…

Slide 76

Slide 76 text

simple

Slide 77

Slide 77 text

Provide Composable Functions simple

Slide 78

Slide 78 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 79

Slide 79 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 80

Slide 80 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 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: Message, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: after 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: Message, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: after 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, *msgs: Message, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: after Library

Slide 84

Slide 84 text

Leverage Language Idioms simple

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, *msgs: Message, **kwargs) -> None: def drain(self) -> None: def close(self) -> None: before 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: before Library

Slide 87

Slide 87 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 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 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 89

Slide 89 text

Leverage Language Idioms (again) simple

Slide 90

Slide 90 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 91

Slide 91 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 92

Slide 92 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 93

Slide 93 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 94

Slide 94 text

Provide Convenience simple

Slide 95

Slide 95 text

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

Slide 96

Slide 96 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 97

Slide 97 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 98

Slide 98 text

Use domain nomenclature Clumsy naming hints at clumsy abstractions 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 99

Slide 99 text

◄◄ REWIND

Slide 100

Slide 100 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 101

Slide 101 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 102

Slide 102 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 103

Slide 103 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() try: pub_client.publish(messages, timeout=1) except chaos_queue.TimeoutError: pass with chaos_queue.SubClient(SUBSCRIPTION) as sub_client: sub_client.create() for message in sub_client.iter(): print(message) process_data(message.data) sub_client.ack(message) User

Slide 104

Slide 104 text

Use domain nomenclature Clumsy naming hints at clumsy abstractions 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 105

Slide 105 text

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

Slide 106

Slide 106 text

rogue.ly/apis Thank you! Lynn Root @roguelynn