Speaker 1: So picture the scenario for a second. You are staring
Speaker 1: at a blank screen on day one of a brand
Speaker 1: new project.
Speaker 2: Oh, the best feeling, right.
Speaker 1: The architecture feels pristine, you are mapping out the logic.
Speaker 1: Those first few features are just you know, flying out
Speaker 1: the door. The boss is thrilled and you feel like
Speaker 1: an absolute genius.
Speaker 2: Yeah, it is the classic honeymoon phase of software development.
Speaker 2: I mean, everything is clean because well, nothing is complex yet.
Speaker 1: But then almost inevitably, you hit the complexity wall. It
Speaker 1: just sneaks up on you.
Speaker 2: It always does.
Speaker 1: Suddenly, adding a simple check box to a screen takes
Speaker 1: two entire weeks. The code that used to be elegant
Speaker 1: is now this tangled, fragile mess of unmaintainable spaghetti.
Speaker 2: Right like, you fix a bug in the payment gateway
Speaker 2: and somehow it breaks the user profile picture.
Speaker 1: Exactly, and development just grinds to a halt. You are
Speaker 1: left pulling your hair out wondering where it all went wrong.
Speaker 1: So today, for this deep dive, we are exploring a
Speaker 1: radical paradigm shift that promises to be the antidote to
Speaker 1: that exact universal nightmare.
Speaker 2: Yeah, we are looking it away to fundamentally rethink how
Speaker 2: we conquer software complexity, specifically in the realm of building
Speaker 2: interactive systems.
Speaker 1: And we are pulling our insights today from the book
Speaker 1: Functional Reactive Programming by Steven Blackheath and Anthony Jones. Our
Speaker 1: mission is to figure out how this concept functional reactive
Speaker 1: programming or FRP, can tear down that complexity wall for good.
Speaker 2: It's a big promise, it is.
Speaker 1: Okay, let's unpack this. To understand the cure, we first
Speaker 1: have to understand the disease. Why do our interactive apps
Speaker 1: always devolve into spaghetti code?
Speaker 2: Well, the authors argue that the disease is rooted in
Speaker 2: the very foundation of how we handle events in almost
Speaker 2: all interactive applications today, whether you are building a user interface,
Speaker 2: a video game, a mobile.
Speaker 1: App, basically anything we use on a daily basis.
Speaker 2: Right, developers rely on something called the observer pattern. You
Speaker 2: might know it as listeners or callbacks. It is by
Speaker 2: far the dominant way to pass messages around in software.
Speaker 1: So if I click a submit button on my screen,
Speaker 1: there is a piece of code like a listener waiting
Speaker 1: in the background for that click, which then triggers a
Speaker 1: callback function to actually process the data. And I mean
Speaker 1: on the surface that sounds like a totally logical way
Speaker 1: to build software.
Speaker 2: It does, and historically it was a massive improvement over
Speaker 2: older methods. It allowed for looser coupling between different parts
Speaker 2: of a program. But the authors identify what they call
Speaker 2: the six plagues of listeners.
Speaker 1: Six plagues.
Speaker 2: Wow, Yeah, there are fundamental, unavoidable flaws in the observer
Speaker 2: pattern that directly cause that complexity.
Speaker 1: Wall, I really want to go through these because I
Speaker 1: have a feeling anyone who has ever written a line
Speaker 1: of code is going to feel seen right now.
Speaker 2: Oh, absolutely so. The first plague is unpredictable order. If
Speaker 2: you have a complex network of listeners all waiting for
Speaker 2: various events, the order in which they actually receive those
Speaker 2: events can depend entirely on the invisible, behind the scenes
Speaker 2: order in which they were originally registered in the computer's memory.
Speaker 1: Wait, so, if I'm building a dashboard and I have
Speaker 1: a listener for a user logging in and another listener
Speaker 1: for loading their profile picture, they might fire in a
Speaker 1: completely random sequence.
Speaker 2: Pretty much.
Speaker 1: That makes me think of like a chaotic group text
Speaker 1: thread where people have bad cell service and messages are
Speaker 1: just arriving out of sync. You are trying to read
Speaker 1: a conversation, but the punchline arrives before the setup.
Speaker 2: That is a perfect mental model for the chaos. And
Speaker 2: imagine if a new person is added to that group
Speaker 2: chat late, they completely missed the original context.
Speaker 1: Oh right.
Speaker 2: In the book they call that the second plague the
Speaker 2: missed first event. It is remarkably easy to accidentally trigger
Speaker 2: an initialization event a millisecond before the specific listener is
Speaker 2: actually registered to hear it.
Speaker 1: Ah. The classic is this thing on bug. The system
Speaker 1: just shouts its status into an empty room exactly.
Speaker 2: It only gets worse from there. The third plague is
Speaker 2: messi state because callbacks are just isolated reactions, They inherently
Speaker 2: push you toward building traditional state machines keep track.
Speaker 1: Of what happened, and as the app grows.
Speaker 2: The human brain just cannot keep track of a massive
Speaker 2: web of shifting states. Yes, too much. Then you add
Speaker 2: the fourth plague, threading issues. The moment you try to
Speaker 2: make all these overlapping listeners ThreadSafe so they can run simultaneously,
Speaker 2: you run headfirst into deadlocks, or.
Speaker 1: Two parts of your program are permanently waiting for each
Speaker 1: other to finish.
Speaker 2: Yeah, which usually results in the entire app just freezing.
Speaker 1: Oh. Man, what about memory, Because in that group text analogy,
Speaker 1: you eventually have people in the chat who haven't spoken
Speaker 1: in three years, but are still getting every single notification.
Speaker 2: That brings us perfectly to the fifth plague, leaking callbacks.
Speaker 2: If a developer forgets to explicitly de register a listener
Speaker 2: when a component is closed, that listener just stays alive
Speaker 2: in the background memory forever.
Speaker 1: So over time, your application just consumes more and more
Speaker 1: RAM until it crashes bingo.
Speaker 2: And finally, the sixth plague is accidental recursion.
Speaker 1: Oh, that sounds bad.
Speaker 2: It is the sequence in which you update local variables
Speaker 2: and notify other listeners can easily cause an infinite loop
Speaker 2: of updates if you aren't incredibly careful, like.
Speaker 1: The dreaded infinite reply all email chain.
Speaker 2: Yes, so, hearing all these plagues together, it is practically
Speaker 2: impossible to piece together a true, reliable sequence of events
Speaker 2: in a large app.
Speaker 1: So this raises an important question, why do we tolerate this?
Speaker 2: Well, the truth is all useful programs are fundamentally state machines.
Speaker 2: They take inputs, they change state, and they produce outputs.
Speaker 2: But as we just outlined, tracking the state of an
Speaker 2: application through an invisible web of callbacks is a nightmare
Speaker 2: for human cognition.
Speaker 1: Right, so we need a completely different way to manage
Speaker 1: state and that brings us to functional reactive programming. Yes,
Speaker 1: but moving to FRP isn't just a matter of downloading
Speaker 1: a new library and learning some new syntax, is it.
Speaker 1: The book describes it as a full blown paradigm shift.
Speaker 2: It really is. You cannot just sprinkle a little FRP
Speaker 2: on top of your listener based code and expect the
Speaker 2: plagues to vanish. You have to change how you think
Speaker 2: about logic itself. Okay, we must move away from operational
Speaker 2: thinking and embrace conceptual thinking, or, to use the industry terms,
Speaker 2: moving from imperative programming to declarative programming.
Speaker 1: I love how the book illustrates this shift. They use
Speaker 1: a lasagna recipe, which is such a brilliant way to
Speaker 1: break down a very abstract computer science concept.
Speaker 2: It's a great analogy.
Speaker 1: So let's look at the operational way first, the old way,
Speaker 1: the way we usually code. It is a sequence of tedious,
Speaker 1: micromanaged steps like heat the oil in a pan, fry
Speaker 1: the onions until golden add ground beef and tomato simmer
Speaker 1: for twenty minutes.
Speaker 2: Right. And if you explain lasagna that way to someone
Speaker 2: who has never seen or tasted it, they have no
Speaker 2: idea what the final dish actually is until they finish
Speaker 2: the grueling process of making it.
Speaker 1: It is just a blind list of chores exactly.
Speaker 2: But the conceptual way, the FRP way, flips that entirely
Speaker 2: on its head. It defines what the thing is bathed
Speaker 2: purely on its dependencies.
Speaker 1: So the declarative recipe is simply lasagna is grated cheese
Speaker 1: on cheese, on bolonnaise, on flat pasta baked right. And
Speaker 1: what does Blannie say, Well, bolonese is a fried onion
Speaker 1: mixed with ground beef and tomato.
Speaker 2: Notice how that subtle shift fundamentally changes the programmer's burden.
Speaker 2: In the operational recipe, the sequence is paramount. If you
Speaker 2: tell the computer to add the cheese before it heats
Speaker 2: the oil, you ruin the dish entirely.
Speaker 1: You're just left with a burnt mess, exactly.
Speaker 2: You, the programmer, carry the heavy mental weight of micromanaging
Speaker 2: the exact timeline of events.
Speaker 1: But in the conceptual recipe, I mean, I don't care
Speaker 1: whether the cheese sauce is prepared before the bolonnaise or
Speaker 1: after or at the exact same time on a different burner.
Speaker 1: I am just mapping out the relationships the dependencies, Yes, exactly,
Speaker 1: But I do have to push back here on behalf
Speaker 1: of the listener. If I am not explicitly telling the
Speaker 1: computer what sequence to do things in, how does the
Speaker 1: machine actually know? Because a computer fundamentally executes instructions sequentially.
Speaker 2: It does, and this is where the magic of the
Speaker 2: FRP engine comes into play. Under the herd, it constructs
Speaker 2: what computer scientists call a directed acyclic graph or a
Speaker 2: dag okay, a dag Yeah. It looks at your conceptual
Speaker 2: recipe and builds a literal mathematical map in memory of
Speaker 2: how everything relates. It knows the Lasagna depends on the
Speaker 2: Bolognaise and the Bolognese depends on the onions. Got it.
Speaker 2: Then the engine performs a topological sort on that graph
Speaker 2: to automatically figure out the perfect sequence of execution for you.
Speaker 1: Wow, that is incredibly librating. So I just draw the
Speaker 1: map and the FRP engine acts as the master chef,
Speaker 1: guaranteeing that everything is processed in the correct natural order exactly.
Speaker 2: Sequence bugs become mathematically impossible because you're no longer writing sequences.
Speaker 1: If we connect this to the bigger picture, all FRP
Speaker 1: code is conceptually just a flow of data. You are
Speaker 1: transforming inputs into outputs without ever manually handling the timeline precisely. Okay,
Speaker 1: So if we are mapping out dependencies instead of writing sequences,
Speaker 1: what are the actual building blocks we are connecting? What
Speaker 1: is our pasta and our cheese in the code?
Speaker 2: In the FARP universe, everything is built on just two
Speaker 2: fundamental data types, streams and cells.
Speaker 1: Let's clearly define those because the entire paradigm rests on them.
Speaker 2: Sure, so, think of a stream as a representation of
Speaker 2: discrete events. It is a flow of data that only
Speaker 2: exists instantaneously at the exact moment it fires. Okay. The
Speaker 2: book gives a really great example of a Japanese board
Speaker 2: game chat window. Imagine you have two buttons for canned messages.
Speaker 2: One that says onnigaishimasu to politely greet your opponent, and
Speaker 2: one that says thank you.
Speaker 1: So clicking one of those buttons is a discrete event.
Speaker 1: It happens in an instant sends a message, and then
Speaker 1: it's gone. That is a.
Speaker 2: Stream, You got it. The other data type is a cell.
Speaker 2: A cell represents a value that changes over time, but
Speaker 2: the golden rule of a cell is that it always
Speaker 2: has a value. You can sample a cell at any
Speaker 2: exact microsecond and it will give you its current state.
Speaker 1: Like a text input field on that same board game
Speaker 1: chat window. If I type I like FRP into the box,
Speaker 1: the box always contains some of text, even if that
Speaker 1: string is just empty space. Initially, the text updates as
Speaker 1: I type, but the box itself is never in a
Speaker 1: state of non existence. It represents the current ongoing state
Speaker 1: of that UI element.
Speaker 2: Precisely, to summarize the difference, streams model state changes. The
Speaker 2: instantaneous events will sells model the state itself.
Speaker 1: Here's where it gets really interesting, though, And I have
Speaker 1: to push back on this cell concept because something doesn't
Speaker 1: add up. If a cell represents a value that changes
Speaker 1: over time, isn't that just a mutable variable wearing a
Speaker 1: fancy disguise. Because anyone who has dabbled in functional programming
Speaker 1: knows that mutable state, you know, variables that change is
Speaker 1: considered the ultimate evil. It makes code unpredictable. How can
Speaker 1: a system claim to be functional if it relies on
Speaker 1: a changing cell?
Speaker 2: You have hit on the central paradox of FRP, and
Speaker 2: the authors actually acknowledge its tension directly. State mutation is
Speaker 2: dangerous in pure functional programming, but event handling in the
Speaker 2: real world is inherently stateful. You can't just pretend state
Speaker 2: doesn't exist when a user is interacting with a screen.
Speaker 2: So to solve this, the book uses this brilliant analogy
Speaker 2: of Pandora's box.
Speaker 1: Okay, walk us through Pandora's box.
Speaker 2: In this context, FRP takes the inherent evil of mutable state,
Speaker 2: chops it up into tiny pieces, and locks each piece
Speaker 2: inside mathematically pure immutable containers. And those containers are your
Speaker 2: streams and cells.
Speaker 1: Oh, I think I see where this is going. The
Speaker 1: container itself cannot be changed, even if the timeline of
Speaker 1: value is flowing through, it represents a changing state.
Speaker 2: That is the crucial distinction. You aren't mutating a variable
Speaker 2: in place like you do in traditional code. You are
Speaker 2: defining an immutable timeline of values.
Speaker 1: Okay, that makes sense.
Speaker 2: Because the stream or cell object itself is locked down
Speaker 2: and pure, the engine can track it safely. The evil
Speaker 2: is contained. You get the mathematical reliability of functional programming
Speaker 2: while still being able to build a dynamic stateful application.
Speaker 1: That's amazing, But keeping the.
Speaker 2: Evil contained requires absolute discipline.
Speaker 1: I assume you were referring to the no cheating rule
Speaker 1: mentioned in the text.
Speaker 2: Exactly to keep Pandora's box closed, every single function you
Speaker 2: pass into your FRP engine must be referentially.
Speaker 1: Transparent, meaning I cannot write a function that secretly reaches
Speaker 1: outside of itself to read a global variable, or write
Speaker 1: a file to the hard drive, or fire off a
Speaker 1: rogue network request.
Speaker 2: No hidden inputs, no hidden outputs. A function must look
Speaker 2: solely at the data handed to it and return a result.
Speaker 2: And if you don't do that, the moment you cheat
Speaker 2: by modifying external state, you break the directed acyclic graph.
Speaker 2: The end, you can no longer guarantee the sequence. Pandora's
Speaker 2: box blasts open, and your code reverts right back to
Speaker 2: the messy plagues of the observer pattern.
Speaker 1: It is fascinating how strict constraints paradoxically give you total freedom.
Speaker 1: Because you are physically prevented from cheating, you just stop
Speaker 1: worrying about side effects. You can trust the math exactly.
Speaker 1: So let's talk about how we actually combine these pure containers.
Speaker 1: The book notes the entire FRP ecosystem and relies on
Speaker 1: just ten primitive operations, just.
Speaker 2: Ten foundational primitives things like map, merge, hole, snapshot lift.
Speaker 2: By combining these ten tools, you can build software of
Speaker 2: infinite complexity.
Speaker 1: Let's look at the snapshot primitive first. The book's example
Speaker 1: for this really clarified things for me. They build a
Speaker 1: mock Latin translator.
Speaker 2: Yeah, adding the suffix us to the end of every
Speaker 2: word to sound falsely authoritative.
Speaker 1: Exactly. So imagine building this. You have a text box
Speaker 1: which is a cell because it holds ongoing state, and
Speaker 1: you have a translate button, which is a stream because
Speaker 1: clicking it is an instantaneous event. You can't just mash
Speaker 1: a stream and a cell together. Instead, you use the
Speaker 1: snapshot primitive. When the button stream fires, it snapshots the
Speaker 1: exact value of the text cell at that specific millisecond,
Speaker 1: runs it through your translation function, and spits out itanius
Speaker 1: udiavus FRPs.
Speaker 2: It's brilliant.
Speaker 1: It is a flawless way to combine an event with
Speaker 1: a state without worrying about.
Speaker 2: Timing bugs, and that completely bypas as is the threading
Speaker 2: issues we discussed earlier. But to truly understand the power
Speaker 2: of FRP. We need to look at the merge primitive. Okay,
Speaker 2: merge takes two separate streams and combines them into one unified.
Speaker 1: Stream, which sounds incredibly simple until you realize that real
Speaker 1: users trigger simultaneous events all the time.
Speaker 2: All the time. So let's apply this to a real
Speaker 2: world scenario from the book. Imagine you are building a
Speaker 2: diagramming tool. You have a canvas showing a triangle and
Speaker 2: an octagon. Okay, the business rules dictate that clicking an
Speaker 2: object selects it, and clicking anywhere else on the canvas
Speaker 2: de selects it.
Speaker 1: So if the triangle is currently selected and my mouse
Speaker 1: clicks the octagon, two logical things have to happen at
Speaker 1: the exact same moment. I have to de select the
Speaker 1: triangle and I have to select the octagon. In a
Speaker 1: traditional listener setup, this is a massive race condition which
Speaker 1: callback fires first. Does the UI momentarily show zero objects
Speaker 1: selected or both objects selected before resolving.
Speaker 2: In traditional code, you just cross your fingers and hope
Speaker 2: the callbacks resolve in the order you intended. But FRP
Speaker 2: handles this through explicit atomic transactions.
Speaker 1: Atomic transaction, Yes, if.
Speaker 2: Two events originate from the exact same root cause. In
Speaker 2: this case, the single mouse click. The FRP engine groups
Speaker 2: them together. They are mathematically simultaneous. The merge primitive actually
Speaker 2: forces the programmer to provide a conflict resolution function.
Speaker 1: Wait, so I literally have to write a rule that
Speaker 1: says if a selection and a de selection happen in
Speaker 1: the same transaction, here is exactly how you combine them.
Speaker 2: You cannot even compile the code without explicitly stating how
Speaker 2: to handle the collision. The engine demands clarity. Wow, Because
Speaker 2: you define the logic upfront, there is no unpredictable order.
Speaker 2: The timeline is guaranteed, and this mathematical guarantee of what
Speaker 2: allows for the holy grail of software design, compositionality.
Speaker 1: The book leans heavily on that term compositionality. Let's break
Speaker 1: down what that actually means for a developer sitting at
Speaker 1: their desk.
Speaker 2: Compositionality means you can say free combined small, perfectly working
Speaker 2: pieces of code to make bigger pieces of code indefinitely.
Speaker 1: Which you can't always do normally.
Speaker 2: No. In traditional listener based programming, combining two perfectly working
Speaker 2: modules often creates brand new bugs just from how they're hidden.
Speaker 2: Callbacks interact with each other an FRP because of referential
Speaker 2: transparency and strict primitives like merge. If the small components work,
Speaker 2: the massive system built from them is mathematically guaranteed to
Speaker 2: work just as reliably.
Speaker 1: So building a mock Latin translator or a diagram tool
Speaker 1: is a great proof of concept. But how does this
Speaker 1: paradigm hold up when the complexity scales? We want to
Speaker 1: see this applied to industrial software.
Speaker 2: Well. To demonstrate scale, the book walks the reader through
Speaker 2: building the logic for a petrol pump stimulator entirely using FRP.
Speaker 1: Functional reactive petroleum. I love the imagery here, and the
Speaker 1: book is very specific about the setup too. You have
Speaker 1: three distinct fuel nozzles, strawberry, lime, and kerosene.
Speaker 2: An interesting flavor selection petrol station, but it serves the
Speaker 2: example well. A petrol pump seems straightforward until you map
Speaker 2: out the overlapping states. You have the three nozzles, three
Speaker 2: different fuel prices. You have a flow meter physically pulsing
Speaker 2: as fuels delivered. You have a display tracking total dollars
Speaker 2: and other tracking total leaders. And you have a keypad
Speaker 2: where a user can punch in a preset dollar limit.
Speaker 2: So the pump automatically stops itself.
Speaker 1: If I were writing this as a traditional state machine,
Speaker 1: tracking all of that would instantly devolve into a nightmare
Speaker 1: of nested if then statements like if the strawberry nozzle
Speaker 1: is lifted and the flow meter pulses, check if the
Speaker 1: preset limit is reached, but only if the limit isn't zero,
Speaker 1: and make sure the line nozzle isn't also lifted. It
Speaker 1: is the definition of spaghetti code, it really is.
Speaker 2: But an FRP you abandon the sequence of checks. You
Speaker 2: write it as a continuous flow of data. The listing
Speaker 2: of the strawberry or kerosene nozzles, those are instantaneous events,
Speaker 2: so they are modeled as streams. Okay, the changing fuel
Speaker 2: prices those told values over time, so they are cells.
Speaker 1: Well, what about the keypad limit or tracking the accumulating
Speaker 1: total of fuel pumped? How do I track a running
Speaker 1: total without creating a mutable variable that I constantly update.
Speaker 1: Doesn't defining a total based on its previous total cause
Speaker 1: that accidental recursion.
Speaker 2: Plague In a standard listeners system, Yes, updating a variable
Speaker 2: based on itself would trigger an endless loop of callbacks.
Speaker 2: But FRP solves this with a mechanism called a cell loop,
Speaker 2: combined with a subtle delay step.
Speaker 1: How does that actually work into the hood without crashing
Speaker 1: The cell.
Speaker 2: Loop essentially tells the FRP engine to calculate the new
Speaker 2: value of this cell for the current transaction, look at
Speaker 2: the value of this cell from the previous transaction plus
Speaker 2: the new input from the flow meter.
Speaker 1: AH.
Speaker 2: Because the engine updates everything atomically in distinct steps, it
Speaker 2: safely handles that forward reference. It breaks the infinite loop
Speaker 2: by separating the timeline into discrete frames.
Speaker 1: So you express the entire petrol pump strictly as mathematical relationships.
Speaker 1: Total dollars is simply e well to leaders delivered multiplied
Speaker 1: by the active fuel price. The data just flows through
Speaker 1: the application logic like fuel thrill pipe.
Speaker 2: And you entirely avoid the state machine nightmare. When the
Speaker 2: product manager asks you to add a new feature like
Speaker 2: making the preset dollar limit automatically slow down the fuel
Speaker 2: flow before it stops.
Speaker 1: You just add a new dependency line to your graph exactly.
Speaker 2: You don't have to rewrite a fragile sequence of fifty callbacks.
Speaker 1: So what does this all mean for you the listener?
Speaker 1: We've covered a massive amount of ground today. We started
Speaker 1: with the complexity wall, the inevitable breakdown of software. We
Speaker 1: explored the six plagues of event driven programming, the misted events,
Speaker 1: the messi states, the memory leaks, yeah, all of it,
Speaker 1: and we've seen how functional reactive programming offers an escape route.
Speaker 2: It offers a rigorously mathematical, declarative escape row. By locking
Speaker 2: the inherent chaos of state inside pure containers, streams, and
Speaker 2: cells and only interacting with them using ten strict primitives,
Speaker 2: we gain the superpower of true compositionality.
Speaker 1: Whether if you are actively coding JABA applications or you
Speaker 1: are just a professional trying to navigate a world overloaded
Speaker 1: with increasingly complex systems, shifting your mental model is an
Speaker 1: incredible tool. Moving your brain from operational sequences desperately micromanaging
Speaker 1: every step of a process, to conceptual dependencies. Defining how
Speaker 1: things fundamentally relate to one another is a massive advantage
Speaker 1: for cutting through the noise.
Speaker 2: It forces you to clearly define what a system is,
Speaker 2: rather than frantically trying to control what a system does
Speaker 2: at every single millisecond.
Speaker 1: Which brings me to a final thought for you, tom
Speaker 1: all over, if functional reactive programming forces us to strictly
Speaker 1: define relationships and dependencies, to prevent chaos in our software.
Speaker 1: What would happen if we applied this declarative mindset to
Speaker 1: our human organizations?
Speaker 2: Oh, that is quite the conceptual leap.
Speaker 1: Think about it. We have all been trapped in that
Speaker 1: chaotic group text thread or that office email chain that
Speaker 1: acts exactly like a broken listener pattern, messages out of order,
Speaker 1: teams working off outdated states, infinite reply.
Speaker 2: All sounds like my Tuesday?
Speaker 1: Right? What if we redefine our office workflows not as rigid,
Speaker 1: micromanaged sequences of operational tasks, but as pure dependent data streams.
Speaker 1: Could applying this declarative mindset finally cure the six plagues
Speaker 1: of modern office communication. Is definitely something to think about
Speaker 1: the next time you hit a complexity wall, whether that
Speaker 1: wall is in your codebase or just in your inbox.