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

Design Pressure

Design Pressure

Avatar for Hynek Schlawack

Hynek Schlawack

May 16, 2025
Tweet

More Decks by Hynek Schlawack

Other Decks in Programming

Transcript

  1. “Until you make the unconscious conscious, it will direct your

    life and you will call it fate.” ― C.G. Jung
  2. if form.valid(): user = db_session.execute( select(User).where(User.id == form.user_id) ) if

    user.something and form.something_else: ruin_life(user) db_session.commit()
  3. Two pieces of code are coupled if they can only

    be understood by looking at both.
  4. [T] [Q] [S] [R] [M] [L] [V] [G] [D] [V]

    [V] [Q] [N] [C] [H] [T] [S] [C] [V] [D] [Z] [Q] [J] [D] [M] [Z] [C] [M] [F] [N] [B] [H] [N] [B] [W] [N] [J] [M] [P] [G] [R] [Z] [Z] [C] [Z] [G] [P] [B] [W] [N] [P] [D] [V] [G] [L] [T] ----------------------------------- 1 2 3 4 5 6 7 8 9 Harbor
  5. [T] [Q] [S] [R] [M] [L] [V] [G] [D] [V]

    [V] [Q] [N] [C] [H] [T] [S] [C] [V] [D] [Z] [Q] [J] [D] [M] [Z] [C] [M] [F] [N] [B] [H] [N] [B] [W] [N] [J] [M] [P] [G] [R] [Z] [Z] [C] [Z] [G] [P] [B] [W] [N] [P] [D] [V] [G] [L] [T] ----------------------------------- 1 2 3 4 5 6 7 8 9 Harbor
  6. [T] [Q] [S] [R] [M] [L] [V] [G] [D] [V]

    [V] [Q] [N] [C] [H] [T] [S] [C] [V] [D] [Z] [Q] [J] [D] [M] [Z] [C] [M] [F] [N] [B] [H] [N] [B] [W] [N] [J] [M] [P] [G] [R] [Z] [Z] [C] [Z] [G] [P] [B] [W] [N] [P] [D] [V] [G] [L] [T] ----------------------------------- 1 2 3 4 5 6 7 8 9 Harbor
  7. [T] [Q] [S] [R] [M] [L] [V] [G] [D] [V]

    [V] [Q] [N] [C] [H] [T] [S] [C] [V] [D] [Z] [Q] [J] [D] [M] [Z] [C] [M] [F] [N] [B] [H] [N] [B] [W] [N] [J] [M] [P] [G] [R] [Z] [Z] [C] [Z] [G] [P] [B] [W] [N] [P] [D] [V] [G] [L] [T] ----------------------------------- 1 2 3 4 5 6 7 8 9 Harbor move 5 from 4 to 9 move 3 from 5 to 1 move 12 from 9 to 6 ... Instructions
  8. [T] [Q] [S] [R] [M] [L] [V] [G] [D] [V]

    [V] [Q] [N] [C] [H] [T] [S] [C] [V] [D] [Z] [Q] [J] [D] [M] [Z] [C] [M] [F] [N] [B] [H] [N] [B] [W] [N] [J] [M] [P] [G] [R] [Z] [Z] [C] [Z] [G] [P] [B] [W] [N] [P] [D] [V] [G] [L] [T] ----------------------------------- 1 2 3 4 5 6 7 8 9 Harbor move 5 from 4 to 9 move 3 from 5 to 1 move 12 from 9 to 6 ... Instructions
  9. [T] [Q] [S] [R] [M] [L] [V] [G] [D] [V]

    [V] [Q] [N] [C] [H] [T] [S] [C] [V] [D] [Z] [Q] [J] [D] [M] [Z] [C] [M] [F] [N] [B] [H] [N] [B] [W] [N] [J] [M] [P] [G] [R] [Z] [Z] [C] [Z] [G] [P] [B] [W] [N] [P] [D] [V] [G] [L] [T] ----------------------------------- 1 2 3 4 5 6 7 8 9 Harbor move 5 from 4 to 9 move 3 from 5 to 1 move 12 from 9 to 6 ... Instructions “You should structure your data so that the problem solves itself.”
  10. @dataclass class Instruction: source: int destination: int count: int @dataclass

    class Harbor: stacks: List[Stack] def execute(self, i: Instruction): source = self.stacks[i.source] destination = self.stacks[i.destination] payload = source.popsome(i.count) destination.extend(payload)
  11. @dataclass class Instruction: source: int destination: int count: int @dataclass

    class Harbor: stacks: List[Stack] def execute(self, i: Instruction): source = self.stacks[i.source] destination = self.stacks[i.destination] payload = source.popsome(i.count) destination.extend(payload)
  12. @dataclass class Instruction: source: int destination: int count: int @dataclass

    class Harbor: stacks: List[Stack] def execute(self, i: Instruction): source = self.stacks[i.source] destination = self.stacks[i.destination] payload = source.popsome(i.count) destination.extend(payload)
  13. @dataclass class Instruction: source: int destination: int count: int @dataclass

    class Harbor: stacks: List[Stack] def execute(self, i: Instruction): source = self.stacks[i.source] destination = self.stacks[i.destination] payload = source.popsome(i.count) destination.extend(payload) @dataclass class Stack: boxes: List[Box] def popsome(self, count: int) -> List[Box]: tail = self.boxes[-count:] self.boxes = self.boxes[:-count] return tail def extend(self, some: List[Box]): self.boxes.extend(some)
  14. @dataclass class Instruction: source: int destination: int count: int @dataclass

    class Harbor: stacks: List[Stack] def execute(self, i: Instruction): source = self.stacks[i.source] destination = self.stacks[i.destination] payload = source.popsome(i.count) destination.extend(payload) @dataclass class Stack: boxes: List[Box] def popsome(self, count: int) -> List[Box]: tail = self.boxes[-count:] self.boxes = self.boxes[:-count] return tail def extend(self, some: List[Box]): self.boxes.extend(some)
  15. @dataclass class Instruction: source: int destination: int count: int @dataclass

    class Harbor: stacks: List[Stack] def execute(self, i: Instruction): source = self.stacks[i.source] destination = self.stacks[i.destination] payload = source.popsome(i.count) destination.extend(payload) @dataclass class Stack: boxes: List[Box] def popsome(self, count: int) -> List[Box]: tail = self.boxes[-count:] self.boxes = self.boxes[:-count] return tail def extend(self, some: List[Box]): self.boxes.extend(some)
  16. @dataclass class Instruction: source: int destination: int count: int @dataclass

    class Harbor: stacks: List[Stack] def execute(self, i: Instruction): source = self.stacks[i.source] destination = self.stacks[i.destination] payload = source.popsome(i.count) destination.extend(payload) @dataclass class Stack: boxes: List[Box] def popsome(self, count: int) -> List[Box]: tail = self.boxes[-count:] self.boxes = self.boxes[:-count] return tail def extend(self, some: List[Box]): self.boxes.extend(some)
  17. “In software engineering, a domain model is a conceptual model

    of the domain that incorporates both behavior and data.” — Wikipedia Domain Model
  18. @dataclass class Instruction: source: int destination: int count: int @dataclass

    class Harbor: stacks: List[Stack] def execute(self, i: Instruction): source = self.stacks[i.source] destination = self.stacks[i.destination] payload = source.popsome(i.count) destination.extend(payload) class Stack: boxes: List[Box] def popsome(self, count: int) -> List[Box]: tail = self.boxes[-count:] self.boxes = self.boxes[:-count] return tail def extend(self, some: List[Box]): self.boxes.extend(some)
  19. @dataclass class Instruction: source: int destination: int count: int @dataclass

    class Harbor: stacks: List[Stack] def execute(self, i: Instruction): source = self.stacks[i.source] destination = self.stacks[i.destination] payload = source.popsome(i.count) destination.extend(payload) class Stack: boxes: List[Box] def popsome(self, count: int) -> List[Box]: tail = self.boxes[-count:] self.boxes = self.boxes[:-count] return tail def extend(self, some: List[Box]): self.boxes.extend(some)
  20. The Darkness def parse_harbor(stackstr: Iterable[str]) -> Harbor: bottomup = list(stackitr)[:-1]

    harbor: Harbor = Harbor([[] for _ in range(N_STACKS)]) for line in bottomup: tokens = [ ''.join(x for _, x in g[1]) for g in groupby( enumerate(line), lambda t: t[0] // N_CHARS_IN_BOX_TOKEN ) ] boxes: List[Box] = [t.strip('[] ') for t in tokens] for box, stack in zip(boxes, harbor.iter_stacks()): if box: stack.append(box) return harbor
  21. “The shitty stu ff should happen on the outside layer

    of your program, that interacts with the "outside world". Once it's inside, organize it into nice data structures so that operations (i.e. code) are as self evident as possible.” — Matthew Drury
  22. Problem #1 best model for web apis ≠ best model

    for business logic ≠ best model for databases
  23. Con f licting goals Web API what is best for

    the user? what is a good standard? (e.g., JSON:API)
  24. Con f licting goals Web API what is best for

    the user? what is a good standard? (e.g., JSON:API) Database schema how to store data e ff ectively? how to normalize? → storage vs performance
  25. Con f licting goals Web API what is best for

    the user? what is a good standard? (e.g., JSON:API) Database schema how to store data e ff ectively? how to normalize? → storage vs performance Domain model how to solve business requirement?
  26. Con f licting goals Web API what is best for

    the user? what is a good standard? (e.g., JSON:API) Database schema how to store data e ff ectively? how to normalize? → storage vs performance Domain model how to solve business requirement?
  27. Con f licting goals Web API what is best for

    the user? what is a good standard? (e.g., JSON:API) Database schema how to store data e ff ectively? how to normalize? → storage vs performance Domain model how to solve business requirement?
  28. type CustomerID = UUID class OpenInvoice: customer: CustomerID amount: Decimal

    due_on: date class PaidInvoice: customer: CustomerID amount: Decimal paid_on: date
  29. type CustomerID = UUID class OpenInvoice: customer: CustomerID amount: Decimal

    due_on: date class PaidInvoice: customer: CustomerID amount: Decimal paid_on: date class OverdueInvoice: customer: CustomerID amount: Decimal overdue_by: int reminder_level: int
  30. type CustomerID = UUID class OpenInvoice: customer: CustomerID amount: Decimal

    due_on: date class PaidInvoice: customer: CustomerID amount: Decimal paid_on: date class OverdueInvoice: customer: CustomerID amount: Decimal overdue_by: int reminder_level: int def release_the_hounds( invs: list[OverdueInvoice] ) -> list[CustomerID]: ...
  31. ”It's ok to have duplicative-looking types for di ff erent

    layers of a project—e.g. ORM, API schema, and business logic. Types need to be easy to change and reason about so we can adapt them to our evolving requirements. Types near the edge (like API schemas and database tables) are inherently less f lexible. It's often better to explicitly map types between these layers rather than create a "franken-type" and allow the least f lexible layer to calcify the rest.” — Adam Montgomery
  32. class PostalAddress: addr1: str | None addr2: str | None

    zip: str | None state: str | None city: str | None country: str
  33. class USAddress: addr1: str addr2: str | None zip: str

    state: str city: str country: str
  34. def format_us_address( addr1: str, addr2: str | None, zip: str,

    state: str, city: str, country: str, ) -> str: ...
  35. “To me, complexity is not about how many keys I

    have to press – it’s about how di ff icult it is to reason about the consequences of what I’m doing.” — me We’re too lazy to type.
  36. Takeaways 1. your business code is sacred 2. protect it

    from your tools 3. write tests; get a better design