Go offline with the Player FM app!
423: Cognitive Strategies for Coders
Manage episode 412774791 series 1401614
Stephanie is back with a book recommendation: "Thinking in Systems" by Donella Meadows. This book has helped to bolster her understanding of complex systems in environmental, organizational, and software contexts, particularly through user interactions and system changes. Joël describes his transformative experience watching last week's total solar eclipse.
Together, they explore how systems thinking influences software development and team dynamics by delving into practical applications in writing and reading code, suggesting that understanding complex systems can aid developers in navigating and optimizing codebases and team interactions.
- Thinking in Systems by Donella Meadows
- Notetaking for developers episode
- Call Graphs
- Flame Graphs
- mermaid.live
- Obsidian
- rails-erd gem
- Decision tables
Transcript:
JOËL: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Joël Quenneville.
STEPHANIE: And I'm Stephanie Minn, and together, we're here to share a bit of what we've learned along the way.
JOËL: So, Stephanie, what's new in your world?
STEPHANIE: I have a book recommendation today [laughs].
JOËL: Oh, I love book recommendations.
STEPHANIE: It's been a little while, so I wanted to share what I've been reading that I think might be interesting to this audience. I'm reading Thinking in Systems by Donella Meadows. Joël, are you familiar with systems thinking theory at all?
JOËL: Very superficially. Hearing people talk about it on, I guess, X, now Twitter.
STEPHANIE: Yeah. Well, what I like about this book is the subtitle is A Primer on Thinking in Systems [chuckles], which is perfect for me as someone who also just kind of understood it very loosely, as just like, oh, like, I dunno, you look at things holistically and look at the stuff, not just its parts but from a higher perspective.
JOËL: Yeah. Is that accurate sort of your pre-book reading overview? Or do you think there's a bigger thing, a bigger idea there that the book unpacks?
STEPHANIE: Yeah. I think I'm only, like, a third of the way through so far. But what I have enjoyed about it is that, you know, in some ways, like, intuitively, that makes a lot of sense about, like, oh yeah, you want to make sure that you see the forest for the trees, right?
But one thing I've been surprised by is how it's also teaching me more technical language to talk about complex systems. And, in this case, she is talking about, essentially, living systems or systems that change over time where things are happening. I think that can be a little bit confusing when we also are, you know, talking about computer systems, but, in this case, you know, systems like environments, or communities, or even, you know, companies or organizations, which is actually where I'm finding a lot of the content really valuable.
But some of the language that I've learned that I am now trying to integrate a little bit more into how I view a lot of just, like, daily problems or experiences involve things like feedback loops that might be reinforcing or balancing and different, like, inputs and output flows and what is driving those things. So, I've appreciated just having more precise language for things that I think I kind of intuited but didn't exactly know how to, like, wrap up in a way to communicate to someone.
JOËL: Do you think the idea of thinking in terms of things like self-balancing versus sort of diverging input loops is something that's useful when actually writing code? Or do you think of it a little bit more in terms of, like, teams and how they organize general problem-solving approaches, things like that?
STEPHANIE: I think the answer is both. I actually gave this quite a bit of thought because I was trying to wrap my head around her definition of a system and how we talk about systems sometimes, like, a codebase, for example. And the conclusion I came to is that, really, it's not just the code static by itself that we care about. It's how it gets exercised, how users use it, how developers change it, how we interact with it when we, like, run tests, for example.
So, that was really helpful in kind of thinking about some of the problems we see in engineering organizations as a result of software being a thing that is used and written by humans, as opposed to it just existing in memories [chuckles] or, like, it's in a storage system somewhere. Like, that means it's kind of lifeless, and it's not changing anymore. But the point of kind of this framework is trying to understand it as it changes.
JOËL: So, kind of that blurry line between humans and computers and where those two overlap is where a lot of that systems thinking almost, like, mental model or vocabulary has been most helpful for you.
STEPHANIE: Yeah, I would say so. So, Joël, what's new in your world?
JOËL: So, I did the thing. I traveled to see the total solar eclipse this past weekend. It was mind-blowing. It was incredibly cool. I really loved it. For any of our listeners who have never seen a solar eclipse, in the coming years, have an opportunity to see one. I'd say it's worth traveling to see because it is really impressive.
STEPHANIE: Cool. What did it look like when it happened, when it was 100% eclipsed?
JOËL: So, what really impressed me was the fact that, like, most of the cool stuff happens in that, like, last half a percent. So, like, 95% eclipsed, still not that impressive. If that's all I'd seen, I would be disappointed. And then, in that last little bit, all of a sudden, everything goes dark. It's sort of, like, that twilight past sunset. You've got a glow on the horizon. The stars are out.
STEPHANIE: Wow.
JOËL: The animals are behaving like it's past sunset. They're getting ready to go to sleep.
STEPHANIE: Whoa.
JOËL: The sun itself is just a black dot with this, like, big fiery ring around it. Like all those pictures, icons, photos you see online, or drawings that look over the top, those things are real. That's what it looks like.
STEPHANIE: Wow, that's really neat. Could you see it without looking through the eclipse viewers?
JOËL: So, when you hit totality, you can look at it with a naked eye, and it is, yeah, magnificent.
STEPHANIE: Oh, that's so cool. How long did it last?
JOËL: So, it depends where you are in the path of totality. I was pretty much dead center. And it lasts, I think, three and a half minutes is what we had.
STEPHANIE: That's so cool. So, for me, here in Chicago, we did not have complete totality. It was about, like, 95%. So, I was watching it, just from that perspective. And I would say, yeah, it was not nearly as cool as what you described. It kind of just was like, oh, it got dark. It almost looked like I was viewing the world through sunglasses.
I did have one of those viewers that I used to, like, look at the sun and see how much of it had been covered. But yeah, it was cool. But what you said, I think now I feel like, wow, I really should have [laughter] traveled. I could have traveled just a few hours, you know, to, like, Indianapolis or something to have been on the path. That would have been really neat. And I don't think the next one will be until 2044 or something like that.
JOËL: Yeah. And that's the thing, right? I think if you're within a few hours of the path of a total eclipse, it is absolutely worth traveling to totality. The downside of that is that everybody else has the same idea. And so, you will be fighting traffic and a lot of things, especially if it goes through some, like, populated areas, like it did this time.
STEPHANIE: Yeah. Well, that's really neat that you got to see that. That's, I don't know, it sounds like not exactly once in a lifetime, but definitely very rare.
JOËL: For sure. I think with this experience now; I would definitely consider traveling again if there's one, like, anywhere near where I live, or, you know, maybe even, like, planning a vacation around going somewhere else to see one because it's short. You know, you're there for three minutes, and you see something cool. But that was really impressive.
So, something that really struck me when you were talking earlier about systems thinking is that you mentioned that it gave you a sort of a new vocabulary to talk about things. It almost gave you a sort of different way of thinking or some other mental models that you could use to apply when you are interacting in that sort of fussy boundary between people and code.
And I think that this idea of having language and having mental models is something that is incredibly valuable for us as programmers in a few different areas. And I'd be curious to see particularly for when we're reading other code, reading code that someone else has written or, you know, yourself from six months ago, do you have any sort of mental models that you like to reach for or techniques that you like to use to sort of give yourself that almost vocabulary to understand what somebody else is trying to do with their code?
STEPHANIE: Yeah, I would say so. You know, as you were talking about, like, how do you read code? I was thinking about how I read code is different from how I would read a book [laughs]. I almost rarely just read everything line by line and, like, file by file, you know, in some order that has been presented to me. I am usually a lot more involved. It's almost, like, more like a choose your own adventure kind of book [chuckles], where it's like, oh, go to this page to check if you want to check out what happened down this code path [chuckles].
JOËL: Right, right. Oh, if you're reading a novel, are you the kind of person that will read the ending first?
STEPHANIE: Absolutely not.
[laughter]
JOËL: You have strong opinions here.
STEPHANIE: Even when I, like, really want to... okay, sometimes I will, like, maybe just kind of flip to the back and just see, like, oh, how many more pages or chapters do I have [laughs] left? If I am itching to know what might happen. But I definitely don't start a book by reading the end. I think there are people who do that, and maybe that works for them, but I don't understand it.
[laughter]
JOËL: But maybe that's the thing that you do with your code.
STEPHANIE: Yeah. When I read code, it's almost always with some kind of intention to understand a particular behavior, usually kind of kicked off by some action, like, done by the user or something automated. And I want to understand that process from start to finish. So, I'm less likely to read a whole class file [chuckles], as opposed to just following method and the messages that are sent along the way in a process.
JOËL: That makes sense. Do you tend to sort of go from kind of the origin point and then follow it down, or sort of the opposite, find some, like, terminal node and then work your way back?
STEPHANIE: Oh.
JOËL: And I could imagine this in a more concrete sense in a Rails app. You find, like, the route that you're going to hit because you know it's a URL, and then you find the controller, and then you read through the action. And then, you maybe follow a service and something like that or look into the view. Or maybe the opposite: there's a particular page that gets rendered. You look at a method, a helper method that gets called in a view, and then you sort of, like, follow a backtrace from there.
STEPHANIE: Yeah, I think both. It depends on what information I have available to me, I think. I can think of, recently, I was trying to figure out the process for which, like, a user in this application I'm working on can downgrade the tier of their account, and I didn't know what to grep for. And so, I asked, like, "Hey, like, what are the entry points for a user being able to do this?"
And someone gave me a couple of routes, and that was great because then I got to see, oh, that this is possible in multiple ways. Like, the user can do it themselves, or the admin can do it, and that was really helpful. Other times, I think I have been able to find a keyword on a page and start from, like, a view or a component, or something like that, and then work upwards.
JOËL: I love that question that you asked, "What are the entry points for this thing?" I feel like that's a fantastic question to sort of ask yourself when you're feeling stuck, but it's also a great question to ask other people that might know.
Do you find that you read code differently when you're just trying to, like, maybe understand a broader subsystem? Maybe you're sort of new to this area and you have to add a feature, as opposed to maybe you're debugging something and trying to understand why things went wrong. Are those two different kinds of reading?
STEPHANIE: Yeah, that's also a great point because I do think there's another time when I've just scanned the file structure of an app and looked at the model's directory and just kind of been like, okay, like, maybe some things are namespaced. And that helps me just know what the main concepts that I have to be dealing with or that I will be dealing with are.
But I find that sometimes less fruitful because of kind of what I mentioned earlier about thinking in systems, where I'm not sure how important those things will be yet because I don't know how they're used. They could not be used at all [laughs]. And then, I think I'm potentially, like, storing information that is not actually relevant in my brain.
JOËL: That's tough, right? Because systems are so big, we can't hold them entirely in our brain. So, sometimes, selectively deciding what will not be loaded in there is just as important as what will.
STEPHANIE: Yes. And I think that is actually advice that I would give to devs who are trying to get better at reading code. And this one's hard because when I am working with more early-career developers, it's hard to figure out, like, what are they seeing? How are they interpreting the code on the page? Because oftentimes, I see that they are getting stuck on the details, whereas I would like to encourage them to just be like, you don't really need to know what's going on in that method right now. Does the method name kind of communicate enough to you, like, what this thing is doing without having to understand all of the details?
But my advice would be to start figuring out what to ignore [laughs] because, like you said, it's impossible to, like, hold all of that information at one time. What do you think about that advice and, like, how do you teach that to someone?
JOËL: I think you're sort of hinting at two different ways of reducing the amount you have to load in your mind. The way I think about it, I think of it sort of spatially, so you can reduce the breadth of things you have to load into your head, so, realize, wait, there's all of these methods, and I don't need to know all of the methods in the file. There's only this one entry point I care about and everything downstream of that, and you just sort of prune everything off to the side, ignore it. That's not relevant right now.
But there's also sort of a depth. How deep of implementation do you really need to have? Maybe you only need to know about the high-level concepts. And then, you sort of, like, do this pruning where you say, "I'm not going to go deeper than this level," because the implementation is not really relevant to what I'm trying to understand right now. I mostly need to know what are these classes and how do they interact with each other? Or something along those lines.
And, ideally, you're may be doing a little bit of both. You probably don't need to go all the way to the deep implementation of every method, but you also don't necessarily need to know all of the high-level concepts and all of the objects in the system that interact. So, being able to prune in sort of both dimensions, breadth and depth, helps you to, I think, narrow the window of what you need to learn.
STEPHANIE: Yeah, that's a really great point. I have a couple more strategies that I just thought about as you were talking about that. One is kind of on the journey to let go of some things that I can't understand in the moment. If they seem important, I will write them down and, like, put them somewhere in a list to come back to later and be like, "This is a thing I don't fully understand yet," and just be okay with that.
I think, for me, there is some anxiety of like, oh, like, what if I'll need to know about it later? And at least putting it down somewhere as like, okay, like, I've done something with that anxious [laughs] energy of, like, recognizing that I don't understand this right now, and that's okay. But I can revisit it later.
And then, another one is almost the opposite, where it's like, what are my landmarks as I'm navigating through a codebase? Like, what are the files that I'm consistently opening? Because so many of the roads lead to this object. Even when I'm kind of going through different paths, it's like, I can hook into, like, the behavior that I'm looking for from these landmark objects or models because they are really important in this domain. So, it's like, I don't necessarily need to remember every step of the way, but if I can recall some of the more important methods, then I can kind of find my way back.
JOËL: Do you just try to, like, memorize those, or do you write them down? Like, how do you make a method or an object a landmark for you?
STEPHANIE: That has felt a little more, like, it becomes more, like, muscle memory, I think, because I'm revisiting them pretty frequently. I don't know, it's somehow the act of repeating, like, going through those files just gets encoded somewhere in my brain [laughs], and I don't have to worry as much about forgetting them.
JOËL: Strengthening that neural pathway.
STEPHANIE: Yeah, exactly.
JOËL: Or whatever is happening in the brain there.
STEPHANIE: [laughs]
JOËL: I like what you were saying earlier, though, about taking notes and sort of almost, like, a breadcrumbs approach. We did an episode almost two years ago where we talked about note-taking for various purposes and note-taking as an exploration exercise, and then note-taking when debugging, where we went deeper into that topic. And I think that would be really relevant to any of our listeners. We'll link that in the show notes.
STEPHANIE: Yeah. Leaving breadcrumbs. That's a great metaphor or just a way to describe it. Because I have a little shorthand for if I am leaving myself notes in a codebase as I'm trying to understand what's happening, and it's just, like, putting my initials in a comment and, like, including some observation or commentary about what I'm seeing or a question.
JOËL: Also, just a kind of meta observation here, but in the last, you know, 10-15 minutes we've been talking about this, we're already creating our own set of metaphors, and language, and mental models around understanding code. We're talking about breadcrumbs, and landmarks, and looking at code through a broad versus deep lens. That's exactly what we're talking about.
STEPHANIE: Joël, do you have any mental models that you use that we haven't really gotten into yet?
JOËL: I don't know if they're mental models per se, but I lean very heavily into diagramming as a form of understanding code. And maybe that's a way of sort of reducing the number of concepts because instead of now sort of thinking in terms of, like, lines of code, I'm thinking in terms of maybe some boxes and arrows, and that's a much higher-level way of looking at a system and can give me some really interesting insights.
And there are a ton of different diagrams you can use for different things, and I guess all of them are based on a different maybe mental model of what a system is. So, for example, I might actually write out the method call graph starting from some endpoint and just sort of saying, "Hey, when I call this method, what are all of the methods downstream that get called? And is there anything interesting at any of those steps?"
Variation on that if you're looking at, let's say, some kind of performance thing would be, like, a flame graph where you have sort of that but then it also shows you the amount of time spent in each of the methods. And that can give you a sense of where your bottlenecks are.
Another one that I really like is thinking in terms of a finite state machine. So, sort of following data, how does it change in response to different events that can come into the system? And I'm not talking about, oh, you're using one of the, like, state machine gems out there for your Rails app. This is more of a way of thinking about programs and how they act.
You can have just a plain, old Rails app, and you're thinking about, okay, well, how does a cart turn into an order, turn into a fulfillment request at the warehouse, turns into a tracking number for shipping? Modeling that as a state machine. And also, you know, can it move back along that path, or is it only linear move forward? Any kind of multi-state form a wizard often has paths where you move back. It's not linear. That very easily can be drawn out as a state machine. So, that is something that I really like to pull out when I'm trying to understand a, like, complex workflow.
STEPHANIE: Yeah, I think we've talked about this before a little bit, or maybe not even a little bit, a lot [laughs]. But I know that you're a big fan of Mermaid.js for creating diagrams in markdown that can be embedded in a pull request description or even in a commit message. When I was hearing you talk about state machines and just all the different paths that can lead to different states, I was like, I bet that's something that you would create using a diagram and stick for yourself and others when sharing code.
JOËL: Yes, Mermaid does support state machines as a graph type, which is really cool.
Another thing that you can do is embed those in tools like Obsidian, which is my current note-taking tool. So, if I'm doing some sort of notes as a sort of exploratory tool, I will often start writing a Mermaid graph directly in line, and it will render and everything. That's really nice. If I'm not in Obsidian and I just need some sort of one-off graph, I'll often lean on Mermaid.live, which just gives you an editor where you can write up some Mermaid code. It will render it, and then you can copy the PNG into somewhere else and share that with other people. So, if I just need a one-off thing to share in Slack or something like that, I like to lean on that.
Another type of diagram that I use pretty frequently is an entity-relationship diagram, so sort of what database tables are related to what others. On larger apps, there's just so many tables, and maybe a bunch of JOINS and things like that, and it's sometimes difficult to get the picture of what is happening, so I'll often draw out a graph of those. Now, it's not worth doing the entire database because that will be huge and overwhelming. So, I'll find, like, five or six tables that are relevant to me and then try to answer the question: How are they related to each other?
STEPHANIE: Yeah, I like that. I was going to ask if you do it manually or if you use a tool because I've worked in various apps that have used the Rails ERD gem that will generate an entity-relationship diagram for you every time the schema changes. But there's something very compelling, to me, about the idea of trying to just figure out if you know the relationships, if you could draw them out, as opposed to having a tool do it for you.
JOËL: Exactly.
STEPHANIE: And I think, like, also, you do have information that might not be encoded in the system. Like, you actually know, oh, these two tables are related, even if no one has defined an association on them. I think that is important in understanding actually how the system is working in real life, I guess.
JOËL: Agreed. So, we've been talking a lot about how we can use different tools, different mental models to take code that somebody else has written and kind of, like, almost read it from disk and load it into our brains. But what about the opposite? We're faced with a business problem, and we want to sort of write it to disk, turn it into code that somebody else will then read or that a machine will execute. I hear that happens occasionally. Are there sort of mental models or ways of approaching tackling a more, like, amorphous problem in the real world and turning that into code? Like, are they just the inverse of what we do when we read code, or are they, like, totally different set of skills?
STEPHANIE: For me personally, I don't follow this framework very strictly, but I think more intuitively how I like to go about it is more behavior-driven where...because that is the language of maybe our cross-functional partners. They're saying like, "Hey, like, when this happens, I want to be able to do this," and I kind of start there. Maybe I'll pick up some of the keywords that they're repeating pretty frequently as like, oh, like, this is a concept.
Actually, lately, the past couple of weeks, I've been test-driving almost all of my code as I work on a totally, like, greenfield feature. And that has been working really well for me, I think, because we did explore more granular, both, like, granular and abstract concepts when we were spiking this feature. And so, we had come up with some domain models. I had kind of thought about, like, how they might interact with each other.
But when you then have to actually, like, code that, there are so many little nuances and things to keep track of that I found test driving things from, like, behavior and user stories. Those are really helpful in keeping me, like, on track to making sure that I didn't just have all these little pieces of domain concepts that then didn't really interact in a meaningful way.
JOËL: Yeah, the sort of very, like, user or customer-centric approach to thinking about what is this app doing? Is a great way to think about it. And I guess the sort of translation of that, that first step of translation into code is some sort of, like, system spec.
STEPHANIE: Yeah, exactly.
JOËL: I like that because, you know, we have all these other abstractions that we use as developers. But at the end of the day, our customers and even, you know, our product people aren't thinking in terms of, like, objects and classes and all these other fun abstractions that we have. They're thinking in terms of behaviors and, you know, maybe subsystems, workflows, things like that. And then it's up to us to translate that into whatever paradigm of our language that we're using.
STEPHANIE: Do you do things differently from me?
JOËL: I don't think that I do it necessarily differently. I think it's one of several tools I have in my tool belt. Something that is similar but from a slightly different angle is inspiring myself with a lot of the ideas from domain-driven design. You know, we've been talking a lot about this idea of, like, mental models and having a vocabulary, things like that, about sort of the way that we work, but that exists at the product level as well. And what if we could encode a lot of that into our application itself?
So, is there a distinction between a subscriber and a payer in our system? Is there specialized vocabulary around different other concepts in the app? Maybe instead of just having those be things that product people talk about, what if we made them actual named entities in the system and have maybe our object graph, at least in some way, reflect the sort of idealized model of what our business actually does?
That often means that you're thinking of things at a higher level because you're thinking of things at the level that our product people are thinking about them. You might be thinking of things in terms of user journeys, or product workflows, or things like that, because you say, "Oh, well, a new payer has been added to this group account. And that has started a subscription, which then means that a user has access to these corporate features that they didn't have when they were in a solo account."
Like, I've just thrown ten different sort of product terms out there that, you know, if there are concepts in our code can help us think about less of the implementation. What does the app do, or how does the app do it? And more in terms of, like, product terms, what does the app do? How do people experience the behavior, or maybe how does data change over the life cycle of the app? So, those perspectives, I think, have helped me distill down sort of more vague product ideas into things that I can then start turning into code.
STEPHANIE: Absolutely. I think one way that this framework ends up falling short, at least for me a little bit sometimes, is making connections between behaviors that are similar but not exactly the same. Or when you think about them in more isolated ways, like, it's easy to miss that, like, they are the same idea and that there is, like, something a bit higher level that you can connect them, that you can create a more abstract class for, even though that's not actually how people talk about the things.
One example I can think of is things like concerns that are both related to domain language but then also, like, kind of specific to how things work in the code as a system because you might not necessarily call something a subscribable from a product perspective. Do you have any thoughts about identifying those pieces?
JOËL: So, what's interesting is I think there's a little bit of, like, layers above and below, the sort of domain layer where you're talking in terms of, like, what the product team would use. When you're doing a lot of the implementation, there will be things that are just, like, that's how we implemented them. They're in the nitty gritty, and they're not terms that the product team would necessarily use.
Things like array and string they're low-level details. We have to use them. That's not really relevant to the world of payers, and subscribers, and things like that. So, they're sort of lower layer. And I think that's totally fine to have things where we sort of have things that are sort of programmer only, as long as they're sort of contained within this higher-level layer because that allows people new to the app to sort of see what are the different things in the application to think about things in a higher level.
It also allows for smoother communication with the product team. So, ideally, you don't have a concept in the app that is the same as something that the product team, but you just both gave it different names, and then that's really annoying. Or maybe the dev team created something that's, like, almost exactly the same as what the product team talks about, but with some, like, slight variations. Now, you're just going to be talking past each other in every planning meeting, and that will be incredibly annoying.
STEPHANIE: Yeah. At one point, when I was trying to communicate, like, async about how a feature works, and there was like the product word for it and then the dev word for it, I would have to type out both [chuckles] because I wanted to make sure that no one was confused about what we were talking about, which was the same thing that just had two names. And yeah, I don't know how many seconds of my life I'll never get back as a result [chuckles].
JOËL: Were these concepts that were identical and had just different names, or was this like, oh, well, our internal subscribed user is almost the same as when product talks about and, I don't know, employee, but our subscribed user has a couple of other extra behaviors that employees don't have, and now there's, like, this weird, like, overlap?
STEPHANIE: Yeah, both situations I have found myself in, but I think this one they were virtually identical. Like, they could be used interchangeably to mean the same thing by people who understand both of those definitions, but the problem was that we still had two words [laughs].
JOËL: Yeah, yeah. I'm a big fan of, where possible, converging on the product team's definition. Although because code forces you to be more precise, sometimes that can then force some conversations with the product team about, like, "Hey, so we've been hand waving around this concept of a subscriber. Turns out we think there's actually two different kinds of concepts at work here: the person who's consuming the content and the person who's paying for it. And are they really the same thing, or should we sort of think about these as two different entities? And, in that case, what should the name be?" And that can force a really, I think, healthy conversation between development and product.
STEPHANIE: Yeah, I like that. You mentioned there was, like, a higher level and a lower level, but I don't think we've gotten to the higher one yet.
JOËL: Yeah. Sometimes, you want to build abstraction sort of over. You're talking about the idea of, like, subscribable things. I think that's where I'm a lot fuzzier. It's much more case-by-case. Where possible, I'd like to introduce some of those things as domain vocabulary so that we'd say, "Well, look, we have a, like, family of products, and they're all subscribable." And maybe, like, the adjective doesn't matter quite as much to our product people, but, you know, because we're using a module in Ruby, we want to lean into the adjective form, and that's fine. But I would at least want some loose connection there.
STEPHANIE: Yeah, that makes sense because I think that ultimately makes for a better product. If we're thinking about, like, how to present a hierarchy of information to a user, like a navigation menu, we would want to group those things that are under that family together, ideally, so that they know how to interact with it.
JOËL: Another thing that I think falls maybe under, like, this higher-level umbrella are things like design patterns. So, maybe because we want to be able to sort of, like, swap things in and out, we're using some form of strategy pattern. That feels like maybe it's a little bit higher level. It interacts with a lot of the domain concepts, but our product team doesn't really need to think in terms of, like, oh, strategies, and swappable things, and, like, flex points in your architecture. So, those would not necessarily be domain vocabulary. Although I could see, like, maybe there's a way where they do get a domain name, and that's great.
STEPHANIE: Oh, I think maybe this is where I disagree with you a little bit. Well, actually, I agreed with what you said at the end [laughs] in terms of how maybe they should be part of the domain vocabulary because I think...I've seen product not fully understand the complexity of the application as it grows over time. And that can lead to sometimes, like, not as great product experience or experience for the user, like, interacting with this product.
And maybe that is something we want to, as developers, if we're starting to see and feel and have maybe even introduced a pattern for...I can't claim to have done this too much, but it's definitely a skill I want to hone in on. But, like, how do I communicate to product folks so that we understand, oh, like, where is it possible for these different types of a subscriber to diverge? Because that is important, I think, in determining the future of a product and, like, where we want to invest in it and where we should focus, like, new features.
JOËL: And oftentimes, when there is that kind of divergence, there probably will be some sort of product-level thinking that needs to happen there. Are we saying, "Hey, we have one of three types of subscribers, and we want to think about that"? Or maybe we want to say, "We have three different ways of processing an application." Maybe it's derived automatically. Maybe it's a dropdown that you have to pick. But let's say it's a dropdown. What do we name that dropdown with the, like, kind of processing that we want to do to an application? The thing that we want to name that dropdown that's probably a good name for that, like, group of strategies, assuming we implement with a strategy pattern. Maybe we're doing it differently.
STEPHANIE: Yeah. The more you talk about that, the more I'm convinced that that's, like, the way I want to be working at least, because you have to know what's there in order to, like, name it. You know, you have to face it, essentially [laughs]. Whereas I think a lot of applications I've worked on fall into the trap of all of those things are obscured way down in the depths of the user flow, where it's like, oh, suddenly, for some reason, you can, like, have a dropdown here that totally changes the behavior, even though you've gotten this far in either the stack trace or even just, like the user journey, as I know you like to branch early in your code.
JOËL: [laughs].
STEPHANIE: But you should also branch early from a user's experience [laughs].
JOËL: In general, I'm just a big fan of having a communication loop between development and product, not only sort of receiving a lot of useful information from the product team about what we want to build. But then because we're encountering this more, like, technical spec that we're writing, have those conversations bubble back to product and say, "Hey, so we talked about a dropdown where there are sort of three different ways of processing an application. Let's talk a little bit more about what it means to have three different ways of processing. And what do we want to name that? Is that accessible to everyone, or are they sort of one-to-one tied with a type of user?"
And all of a sudden, that has just generated probably a lot of questions that product never even thought to ask because they're working on an infinite canvas of possibilities. And it's really helped you as a developer to have better names to write your code and sort of better sketch out the boundaries of the problem you're trying to solve. So, I think it's a really healthy loop to have. I strongly encourage it.
So, we've spent a lot of time talking about thinking about behavior and things like the domain-driven design movement. But a few other things I want to shout out as being really helpful, one is an exercise where you take a problem statement and just underline all of the nouns. That is a great way to get a sense of, like, what is going on here.
More generally, I think a lot of what we're talking about falls under the umbrella of what you might call analysis. And so, digging into different analytic techniques can be a great way to better understand the problem that you're working through. One such tool would be decision tables. So, you have a problem, and you say, "Well, given these inputs, what should the outputs be?"
STEPHANIE: Cool. If there were any techniques or tools that we missed in terms of how you load code in your brain or generate code from your brain [laughs], we would love to know. You can write in to us at hosts@bikeshed.fm.
JOËL: On that note, shall we wrap up?
STEPHANIE: Let's wrap up.
STEPHANIE: Show notes for this episode can be found at bikeshed.fm.
JOËL: This show has been produced and edited by Mandy Moore.
STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show.
JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter.
STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email.
JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week.
ALL: Byeeeeeeee!!!!!!!
AD:
Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us.
More info on our website at: tbot.io/referral. Or you can email us at: referrals@thoughtbot.com with any questions.
439 episodes
Manage episode 412774791 series 1401614
Stephanie is back with a book recommendation: "Thinking in Systems" by Donella Meadows. This book has helped to bolster her understanding of complex systems in environmental, organizational, and software contexts, particularly through user interactions and system changes. Joël describes his transformative experience watching last week's total solar eclipse.
Together, they explore how systems thinking influences software development and team dynamics by delving into practical applications in writing and reading code, suggesting that understanding complex systems can aid developers in navigating and optimizing codebases and team interactions.
- Thinking in Systems by Donella Meadows
- Notetaking for developers episode
- Call Graphs
- Flame Graphs
- mermaid.live
- Obsidian
- rails-erd gem
- Decision tables
Transcript:
JOËL: Hello and welcome to another episode of The Bike Shed, a weekly podcast from your friends at thoughtbot about developing great software. I'm Joël Quenneville.
STEPHANIE: And I'm Stephanie Minn, and together, we're here to share a bit of what we've learned along the way.
JOËL: So, Stephanie, what's new in your world?
STEPHANIE: I have a book recommendation today [laughs].
JOËL: Oh, I love book recommendations.
STEPHANIE: It's been a little while, so I wanted to share what I've been reading that I think might be interesting to this audience. I'm reading Thinking in Systems by Donella Meadows. Joël, are you familiar with systems thinking theory at all?
JOËL: Very superficially. Hearing people talk about it on, I guess, X, now Twitter.
STEPHANIE: Yeah. Well, what I like about this book is the subtitle is A Primer on Thinking in Systems [chuckles], which is perfect for me as someone who also just kind of understood it very loosely, as just like, oh, like, I dunno, you look at things holistically and look at the stuff, not just its parts but from a higher perspective.
JOËL: Yeah. Is that accurate sort of your pre-book reading overview? Or do you think there's a bigger thing, a bigger idea there that the book unpacks?
STEPHANIE: Yeah. I think I'm only, like, a third of the way through so far. But what I have enjoyed about it is that, you know, in some ways, like, intuitively, that makes a lot of sense about, like, oh yeah, you want to make sure that you see the forest for the trees, right?
But one thing I've been surprised by is how it's also teaching me more technical language to talk about complex systems. And, in this case, she is talking about, essentially, living systems or systems that change over time where things are happening. I think that can be a little bit confusing when we also are, you know, talking about computer systems, but, in this case, you know, systems like environments, or communities, or even, you know, companies or organizations, which is actually where I'm finding a lot of the content really valuable.
But some of the language that I've learned that I am now trying to integrate a little bit more into how I view a lot of just, like, daily problems or experiences involve things like feedback loops that might be reinforcing or balancing and different, like, inputs and output flows and what is driving those things. So, I've appreciated just having more precise language for things that I think I kind of intuited but didn't exactly know how to, like, wrap up in a way to communicate to someone.
JOËL: Do you think the idea of thinking in terms of things like self-balancing versus sort of diverging input loops is something that's useful when actually writing code? Or do you think of it a little bit more in terms of, like, teams and how they organize general problem-solving approaches, things like that?
STEPHANIE: I think the answer is both. I actually gave this quite a bit of thought because I was trying to wrap my head around her definition of a system and how we talk about systems sometimes, like, a codebase, for example. And the conclusion I came to is that, really, it's not just the code static by itself that we care about. It's how it gets exercised, how users use it, how developers change it, how we interact with it when we, like, run tests, for example.
So, that was really helpful in kind of thinking about some of the problems we see in engineering organizations as a result of software being a thing that is used and written by humans, as opposed to it just existing in memories [chuckles] or, like, it's in a storage system somewhere. Like, that means it's kind of lifeless, and it's not changing anymore. But the point of kind of this framework is trying to understand it as it changes.
JOËL: So, kind of that blurry line between humans and computers and where those two overlap is where a lot of that systems thinking almost, like, mental model or vocabulary has been most helpful for you.
STEPHANIE: Yeah, I would say so. So, Joël, what's new in your world?
JOËL: So, I did the thing. I traveled to see the total solar eclipse this past weekend. It was mind-blowing. It was incredibly cool. I really loved it. For any of our listeners who have never seen a solar eclipse, in the coming years, have an opportunity to see one. I'd say it's worth traveling to see because it is really impressive.
STEPHANIE: Cool. What did it look like when it happened, when it was 100% eclipsed?
JOËL: So, what really impressed me was the fact that, like, most of the cool stuff happens in that, like, last half a percent. So, like, 95% eclipsed, still not that impressive. If that's all I'd seen, I would be disappointed. And then, in that last little bit, all of a sudden, everything goes dark. It's sort of, like, that twilight past sunset. You've got a glow on the horizon. The stars are out.
STEPHANIE: Wow.
JOËL: The animals are behaving like it's past sunset. They're getting ready to go to sleep.
STEPHANIE: Whoa.
JOËL: The sun itself is just a black dot with this, like, big fiery ring around it. Like all those pictures, icons, photos you see online, or drawings that look over the top, those things are real. That's what it looks like.
STEPHANIE: Wow, that's really neat. Could you see it without looking through the eclipse viewers?
JOËL: So, when you hit totality, you can look at it with a naked eye, and it is, yeah, magnificent.
STEPHANIE: Oh, that's so cool. How long did it last?
JOËL: So, it depends where you are in the path of totality. I was pretty much dead center. And it lasts, I think, three and a half minutes is what we had.
STEPHANIE: That's so cool. So, for me, here in Chicago, we did not have complete totality. It was about, like, 95%. So, I was watching it, just from that perspective. And I would say, yeah, it was not nearly as cool as what you described. It kind of just was like, oh, it got dark. It almost looked like I was viewing the world through sunglasses.
I did have one of those viewers that I used to, like, look at the sun and see how much of it had been covered. But yeah, it was cool. But what you said, I think now I feel like, wow, I really should have [laughter] traveled. I could have traveled just a few hours, you know, to, like, Indianapolis or something to have been on the path. That would have been really neat. And I don't think the next one will be until 2044 or something like that.
JOËL: Yeah. And that's the thing, right? I think if you're within a few hours of the path of a total eclipse, it is absolutely worth traveling to totality. The downside of that is that everybody else has the same idea. And so, you will be fighting traffic and a lot of things, especially if it goes through some, like, populated areas, like it did this time.
STEPHANIE: Yeah. Well, that's really neat that you got to see that. That's, I don't know, it sounds like not exactly once in a lifetime, but definitely very rare.
JOËL: For sure. I think with this experience now; I would definitely consider traveling again if there's one, like, anywhere near where I live, or, you know, maybe even, like, planning a vacation around going somewhere else to see one because it's short. You know, you're there for three minutes, and you see something cool. But that was really impressive.
So, something that really struck me when you were talking earlier about systems thinking is that you mentioned that it gave you a sort of a new vocabulary to talk about things. It almost gave you a sort of different way of thinking or some other mental models that you could use to apply when you are interacting in that sort of fussy boundary between people and code.
And I think that this idea of having language and having mental models is something that is incredibly valuable for us as programmers in a few different areas. And I'd be curious to see particularly for when we're reading other code, reading code that someone else has written or, you know, yourself from six months ago, do you have any sort of mental models that you like to reach for or techniques that you like to use to sort of give yourself that almost vocabulary to understand what somebody else is trying to do with their code?
STEPHANIE: Yeah, I would say so. You know, as you were talking about, like, how do you read code? I was thinking about how I read code is different from how I would read a book [laughs]. I almost rarely just read everything line by line and, like, file by file, you know, in some order that has been presented to me. I am usually a lot more involved. It's almost, like, more like a choose your own adventure kind of book [chuckles], where it's like, oh, go to this page to check if you want to check out what happened down this code path [chuckles].
JOËL: Right, right. Oh, if you're reading a novel, are you the kind of person that will read the ending first?
STEPHANIE: Absolutely not.
[laughter]
JOËL: You have strong opinions here.
STEPHANIE: Even when I, like, really want to... okay, sometimes I will, like, maybe just kind of flip to the back and just see, like, oh, how many more pages or chapters do I have [laughs] left? If I am itching to know what might happen. But I definitely don't start a book by reading the end. I think there are people who do that, and maybe that works for them, but I don't understand it.
[laughter]
JOËL: But maybe that's the thing that you do with your code.
STEPHANIE: Yeah. When I read code, it's almost always with some kind of intention to understand a particular behavior, usually kind of kicked off by some action, like, done by the user or something automated. And I want to understand that process from start to finish. So, I'm less likely to read a whole class file [chuckles], as opposed to just following method and the messages that are sent along the way in a process.
JOËL: That makes sense. Do you tend to sort of go from kind of the origin point and then follow it down, or sort of the opposite, find some, like, terminal node and then work your way back?
STEPHANIE: Oh.
JOËL: And I could imagine this in a more concrete sense in a Rails app. You find, like, the route that you're going to hit because you know it's a URL, and then you find the controller, and then you read through the action. And then, you maybe follow a service and something like that or look into the view. Or maybe the opposite: there's a particular page that gets rendered. You look at a method, a helper method that gets called in a view, and then you sort of, like, follow a backtrace from there.
STEPHANIE: Yeah, I think both. It depends on what information I have available to me, I think. I can think of, recently, I was trying to figure out the process for which, like, a user in this application I'm working on can downgrade the tier of their account, and I didn't know what to grep for. And so, I asked, like, "Hey, like, what are the entry points for a user being able to do this?"
And someone gave me a couple of routes, and that was great because then I got to see, oh, that this is possible in multiple ways. Like, the user can do it themselves, or the admin can do it, and that was really helpful. Other times, I think I have been able to find a keyword on a page and start from, like, a view or a component, or something like that, and then work upwards.
JOËL: I love that question that you asked, "What are the entry points for this thing?" I feel like that's a fantastic question to sort of ask yourself when you're feeling stuck, but it's also a great question to ask other people that might know.
Do you find that you read code differently when you're just trying to, like, maybe understand a broader subsystem? Maybe you're sort of new to this area and you have to add a feature, as opposed to maybe you're debugging something and trying to understand why things went wrong. Are those two different kinds of reading?
STEPHANIE: Yeah, that's also a great point because I do think there's another time when I've just scanned the file structure of an app and looked at the model's directory and just kind of been like, okay, like, maybe some things are namespaced. And that helps me just know what the main concepts that I have to be dealing with or that I will be dealing with are.
But I find that sometimes less fruitful because of kind of what I mentioned earlier about thinking in systems, where I'm not sure how important those things will be yet because I don't know how they're used. They could not be used at all [laughs]. And then, I think I'm potentially, like, storing information that is not actually relevant in my brain.
JOËL: That's tough, right? Because systems are so big, we can't hold them entirely in our brain. So, sometimes, selectively deciding what will not be loaded in there is just as important as what will.
STEPHANIE: Yes. And I think that is actually advice that I would give to devs who are trying to get better at reading code. And this one's hard because when I am working with more early-career developers, it's hard to figure out, like, what are they seeing? How are they interpreting the code on the page? Because oftentimes, I see that they are getting stuck on the details, whereas I would like to encourage them to just be like, you don't really need to know what's going on in that method right now. Does the method name kind of communicate enough to you, like, what this thing is doing without having to understand all of the details?
But my advice would be to start figuring out what to ignore [laughs] because, like you said, it's impossible to, like, hold all of that information at one time. What do you think about that advice and, like, how do you teach that to someone?
JOËL: I think you're sort of hinting at two different ways of reducing the amount you have to load in your mind. The way I think about it, I think of it sort of spatially, so you can reduce the breadth of things you have to load into your head, so, realize, wait, there's all of these methods, and I don't need to know all of the methods in the file. There's only this one entry point I care about and everything downstream of that, and you just sort of prune everything off to the side, ignore it. That's not relevant right now.
But there's also sort of a depth. How deep of implementation do you really need to have? Maybe you only need to know about the high-level concepts. And then, you sort of, like, do this pruning where you say, "I'm not going to go deeper than this level," because the implementation is not really relevant to what I'm trying to understand right now. I mostly need to know what are these classes and how do they interact with each other? Or something along those lines.
And, ideally, you're may be doing a little bit of both. You probably don't need to go all the way to the deep implementation of every method, but you also don't necessarily need to know all of the high-level concepts and all of the objects in the system that interact. So, being able to prune in sort of both dimensions, breadth and depth, helps you to, I think, narrow the window of what you need to learn.
STEPHANIE: Yeah, that's a really great point. I have a couple more strategies that I just thought about as you were talking about that. One is kind of on the journey to let go of some things that I can't understand in the moment. If they seem important, I will write them down and, like, put them somewhere in a list to come back to later and be like, "This is a thing I don't fully understand yet," and just be okay with that.
I think, for me, there is some anxiety of like, oh, like, what if I'll need to know about it later? And at least putting it down somewhere as like, okay, like, I've done something with that anxious [laughs] energy of, like, recognizing that I don't understand this right now, and that's okay. But I can revisit it later.
And then, another one is almost the opposite, where it's like, what are my landmarks as I'm navigating through a codebase? Like, what are the files that I'm consistently opening? Because so many of the roads lead to this object. Even when I'm kind of going through different paths, it's like, I can hook into, like, the behavior that I'm looking for from these landmark objects or models because they are really important in this domain. So, it's like, I don't necessarily need to remember every step of the way, but if I can recall some of the more important methods, then I can kind of find my way back.
JOËL: Do you just try to, like, memorize those, or do you write them down? Like, how do you make a method or an object a landmark for you?
STEPHANIE: That has felt a little more, like, it becomes more, like, muscle memory, I think, because I'm revisiting them pretty frequently. I don't know, it's somehow the act of repeating, like, going through those files just gets encoded somewhere in my brain [laughs], and I don't have to worry as much about forgetting them.
JOËL: Strengthening that neural pathway.
STEPHANIE: Yeah, exactly.
JOËL: Or whatever is happening in the brain there.
STEPHANIE: [laughs]
JOËL: I like what you were saying earlier, though, about taking notes and sort of almost, like, a breadcrumbs approach. We did an episode almost two years ago where we talked about note-taking for various purposes and note-taking as an exploration exercise, and then note-taking when debugging, where we went deeper into that topic. And I think that would be really relevant to any of our listeners. We'll link that in the show notes.
STEPHANIE: Yeah. Leaving breadcrumbs. That's a great metaphor or just a way to describe it. Because I have a little shorthand for if I am leaving myself notes in a codebase as I'm trying to understand what's happening, and it's just, like, putting my initials in a comment and, like, including some observation or commentary about what I'm seeing or a question.
JOËL: Also, just a kind of meta observation here, but in the last, you know, 10-15 minutes we've been talking about this, we're already creating our own set of metaphors, and language, and mental models around understanding code. We're talking about breadcrumbs, and landmarks, and looking at code through a broad versus deep lens. That's exactly what we're talking about.
STEPHANIE: Joël, do you have any mental models that you use that we haven't really gotten into yet?
JOËL: I don't know if they're mental models per se, but I lean very heavily into diagramming as a form of understanding code. And maybe that's a way of sort of reducing the number of concepts because instead of now sort of thinking in terms of, like, lines of code, I'm thinking in terms of maybe some boxes and arrows, and that's a much higher-level way of looking at a system and can give me some really interesting insights.
And there are a ton of different diagrams you can use for different things, and I guess all of them are based on a different maybe mental model of what a system is. So, for example, I might actually write out the method call graph starting from some endpoint and just sort of saying, "Hey, when I call this method, what are all of the methods downstream that get called? And is there anything interesting at any of those steps?"
Variation on that if you're looking at, let's say, some kind of performance thing would be, like, a flame graph where you have sort of that but then it also shows you the amount of time spent in each of the methods. And that can give you a sense of where your bottlenecks are.
Another one that I really like is thinking in terms of a finite state machine. So, sort of following data, how does it change in response to different events that can come into the system? And I'm not talking about, oh, you're using one of the, like, state machine gems out there for your Rails app. This is more of a way of thinking about programs and how they act.
You can have just a plain, old Rails app, and you're thinking about, okay, well, how does a cart turn into an order, turn into a fulfillment request at the warehouse, turns into a tracking number for shipping? Modeling that as a state machine. And also, you know, can it move back along that path, or is it only linear move forward? Any kind of multi-state form a wizard often has paths where you move back. It's not linear. That very easily can be drawn out as a state machine. So, that is something that I really like to pull out when I'm trying to understand a, like, complex workflow.
STEPHANIE: Yeah, I think we've talked about this before a little bit, or maybe not even a little bit, a lot [laughs]. But I know that you're a big fan of Mermaid.js for creating diagrams in markdown that can be embedded in a pull request description or even in a commit message. When I was hearing you talk about state machines and just all the different paths that can lead to different states, I was like, I bet that's something that you would create using a diagram and stick for yourself and others when sharing code.
JOËL: Yes, Mermaid does support state machines as a graph type, which is really cool.
Another thing that you can do is embed those in tools like Obsidian, which is my current note-taking tool. So, if I'm doing some sort of notes as a sort of exploratory tool, I will often start writing a Mermaid graph directly in line, and it will render and everything. That's really nice. If I'm not in Obsidian and I just need some sort of one-off graph, I'll often lean on Mermaid.live, which just gives you an editor where you can write up some Mermaid code. It will render it, and then you can copy the PNG into somewhere else and share that with other people. So, if I just need a one-off thing to share in Slack or something like that, I like to lean on that.
Another type of diagram that I use pretty frequently is an entity-relationship diagram, so sort of what database tables are related to what others. On larger apps, there's just so many tables, and maybe a bunch of JOINS and things like that, and it's sometimes difficult to get the picture of what is happening, so I'll often draw out a graph of those. Now, it's not worth doing the entire database because that will be huge and overwhelming. So, I'll find, like, five or six tables that are relevant to me and then try to answer the question: How are they related to each other?
STEPHANIE: Yeah, I like that. I was going to ask if you do it manually or if you use a tool because I've worked in various apps that have used the Rails ERD gem that will generate an entity-relationship diagram for you every time the schema changes. But there's something very compelling, to me, about the idea of trying to just figure out if you know the relationships, if you could draw them out, as opposed to having a tool do it for you.
JOËL: Exactly.
STEPHANIE: And I think, like, also, you do have information that might not be encoded in the system. Like, you actually know, oh, these two tables are related, even if no one has defined an association on them. I think that is important in understanding actually how the system is working in real life, I guess.
JOËL: Agreed. So, we've been talking a lot about how we can use different tools, different mental models to take code that somebody else has written and kind of, like, almost read it from disk and load it into our brains. But what about the opposite? We're faced with a business problem, and we want to sort of write it to disk, turn it into code that somebody else will then read or that a machine will execute. I hear that happens occasionally. Are there sort of mental models or ways of approaching tackling a more, like, amorphous problem in the real world and turning that into code? Like, are they just the inverse of what we do when we read code, or are they, like, totally different set of skills?
STEPHANIE: For me personally, I don't follow this framework very strictly, but I think more intuitively how I like to go about it is more behavior-driven where...because that is the language of maybe our cross-functional partners. They're saying like, "Hey, like, when this happens, I want to be able to do this," and I kind of start there. Maybe I'll pick up some of the keywords that they're repeating pretty frequently as like, oh, like, this is a concept.
Actually, lately, the past couple of weeks, I've been test-driving almost all of my code as I work on a totally, like, greenfield feature. And that has been working really well for me, I think, because we did explore more granular, both, like, granular and abstract concepts when we were spiking this feature. And so, we had come up with some domain models. I had kind of thought about, like, how they might interact with each other.
But when you then have to actually, like, code that, there are so many little nuances and things to keep track of that I found test driving things from, like, behavior and user stories. Those are really helpful in keeping me, like, on track to making sure that I didn't just have all these little pieces of domain concepts that then didn't really interact in a meaningful way.
JOËL: Yeah, the sort of very, like, user or customer-centric approach to thinking about what is this app doing? Is a great way to think about it. And I guess the sort of translation of that, that first step of translation into code is some sort of, like, system spec.
STEPHANIE: Yeah, exactly.
JOËL: I like that because, you know, we have all these other abstractions that we use as developers. But at the end of the day, our customers and even, you know, our product people aren't thinking in terms of, like, objects and classes and all these other fun abstractions that we have. They're thinking in terms of behaviors and, you know, maybe subsystems, workflows, things like that. And then it's up to us to translate that into whatever paradigm of our language that we're using.
STEPHANIE: Do you do things differently from me?
JOËL: I don't think that I do it necessarily differently. I think it's one of several tools I have in my tool belt. Something that is similar but from a slightly different angle is inspiring myself with a lot of the ideas from domain-driven design. You know, we've been talking a lot about this idea of, like, mental models and having a vocabulary, things like that, about sort of the way that we work, but that exists at the product level as well. And what if we could encode a lot of that into our application itself?
So, is there a distinction between a subscriber and a payer in our system? Is there specialized vocabulary around different other concepts in the app? Maybe instead of just having those be things that product people talk about, what if we made them actual named entities in the system and have maybe our object graph, at least in some way, reflect the sort of idealized model of what our business actually does?
That often means that you're thinking of things at a higher level because you're thinking of things at the level that our product people are thinking about them. You might be thinking of things in terms of user journeys, or product workflows, or things like that, because you say, "Oh, well, a new payer has been added to this group account. And that has started a subscription, which then means that a user has access to these corporate features that they didn't have when they were in a solo account."
Like, I've just thrown ten different sort of product terms out there that, you know, if there are concepts in our code can help us think about less of the implementation. What does the app do, or how does the app do it? And more in terms of, like, product terms, what does the app do? How do people experience the behavior, or maybe how does data change over the life cycle of the app? So, those perspectives, I think, have helped me distill down sort of more vague product ideas into things that I can then start turning into code.
STEPHANIE: Absolutely. I think one way that this framework ends up falling short, at least for me a little bit sometimes, is making connections between behaviors that are similar but not exactly the same. Or when you think about them in more isolated ways, like, it's easy to miss that, like, they are the same idea and that there is, like, something a bit higher level that you can connect them, that you can create a more abstract class for, even though that's not actually how people talk about the things.
One example I can think of is things like concerns that are both related to domain language but then also, like, kind of specific to how things work in the code as a system because you might not necessarily call something a subscribable from a product perspective. Do you have any thoughts about identifying those pieces?
JOËL: So, what's interesting is I think there's a little bit of, like, layers above and below, the sort of domain layer where you're talking in terms of, like, what the product team would use. When you're doing a lot of the implementation, there will be things that are just, like, that's how we implemented them. They're in the nitty gritty, and they're not terms that the product team would necessarily use.
Things like array and string they're low-level details. We have to use them. That's not really relevant to the world of payers, and subscribers, and things like that. So, they're sort of lower layer. And I think that's totally fine to have things where we sort of have things that are sort of programmer only, as long as they're sort of contained within this higher-level layer because that allows people new to the app to sort of see what are the different things in the application to think about things in a higher level.
It also allows for smoother communication with the product team. So, ideally, you don't have a concept in the app that is the same as something that the product team, but you just both gave it different names, and then that's really annoying. Or maybe the dev team created something that's, like, almost exactly the same as what the product team talks about, but with some, like, slight variations. Now, you're just going to be talking past each other in every planning meeting, and that will be incredibly annoying.
STEPHANIE: Yeah. At one point, when I was trying to communicate, like, async about how a feature works, and there was like the product word for it and then the dev word for it, I would have to type out both [chuckles] because I wanted to make sure that no one was confused about what we were talking about, which was the same thing that just had two names. And yeah, I don't know how many seconds of my life I'll never get back as a result [chuckles].
JOËL: Were these concepts that were identical and had just different names, or was this like, oh, well, our internal subscribed user is almost the same as when product talks about and, I don't know, employee, but our subscribed user has a couple of other extra behaviors that employees don't have, and now there's, like, this weird, like, overlap?
STEPHANIE: Yeah, both situations I have found myself in, but I think this one they were virtually identical. Like, they could be used interchangeably to mean the same thing by people who understand both of those definitions, but the problem was that we still had two words [laughs].
JOËL: Yeah, yeah. I'm a big fan of, where possible, converging on the product team's definition. Although because code forces you to be more precise, sometimes that can then force some conversations with the product team about, like, "Hey, so we've been hand waving around this concept of a subscriber. Turns out we think there's actually two different kinds of concepts at work here: the person who's consuming the content and the person who's paying for it. And are they really the same thing, or should we sort of think about these as two different entities? And, in that case, what should the name be?" And that can force a really, I think, healthy conversation between development and product.
STEPHANIE: Yeah, I like that. You mentioned there was, like, a higher level and a lower level, but I don't think we've gotten to the higher one yet.
JOËL: Yeah. Sometimes, you want to build abstraction sort of over. You're talking about the idea of, like, subscribable things. I think that's where I'm a lot fuzzier. It's much more case-by-case. Where possible, I'd like to introduce some of those things as domain vocabulary so that we'd say, "Well, look, we have a, like, family of products, and they're all subscribable." And maybe, like, the adjective doesn't matter quite as much to our product people, but, you know, because we're using a module in Ruby, we want to lean into the adjective form, and that's fine. But I would at least want some loose connection there.
STEPHANIE: Yeah, that makes sense because I think that ultimately makes for a better product. If we're thinking about, like, how to present a hierarchy of information to a user, like a navigation menu, we would want to group those things that are under that family together, ideally, so that they know how to interact with it.
JOËL: Another thing that I think falls maybe under, like, this higher-level umbrella are things like design patterns. So, maybe because we want to be able to sort of, like, swap things in and out, we're using some form of strategy pattern. That feels like maybe it's a little bit higher level. It interacts with a lot of the domain concepts, but our product team doesn't really need to think in terms of, like, oh, strategies, and swappable things, and, like, flex points in your architecture. So, those would not necessarily be domain vocabulary. Although I could see, like, maybe there's a way where they do get a domain name, and that's great.
STEPHANIE: Oh, I think maybe this is where I disagree with you a little bit. Well, actually, I agreed with what you said at the end [laughs] in terms of how maybe they should be part of the domain vocabulary because I think...I've seen product not fully understand the complexity of the application as it grows over time. And that can lead to sometimes, like, not as great product experience or experience for the user, like, interacting with this product.
And maybe that is something we want to, as developers, if we're starting to see and feel and have maybe even introduced a pattern for...I can't claim to have done this too much, but it's definitely a skill I want to hone in on. But, like, how do I communicate to product folks so that we understand, oh, like, where is it possible for these different types of a subscriber to diverge? Because that is important, I think, in determining the future of a product and, like, where we want to invest in it and where we should focus, like, new features.
JOËL: And oftentimes, when there is that kind of divergence, there probably will be some sort of product-level thinking that needs to happen there. Are we saying, "Hey, we have one of three types of subscribers, and we want to think about that"? Or maybe we want to say, "We have three different ways of processing an application." Maybe it's derived automatically. Maybe it's a dropdown that you have to pick. But let's say it's a dropdown. What do we name that dropdown with the, like, kind of processing that we want to do to an application? The thing that we want to name that dropdown that's probably a good name for that, like, group of strategies, assuming we implement with a strategy pattern. Maybe we're doing it differently.
STEPHANIE: Yeah. The more you talk about that, the more I'm convinced that that's, like, the way I want to be working at least, because you have to know what's there in order to, like, name it. You know, you have to face it, essentially [laughs]. Whereas I think a lot of applications I've worked on fall into the trap of all of those things are obscured way down in the depths of the user flow, where it's like, oh, suddenly, for some reason, you can, like, have a dropdown here that totally changes the behavior, even though you've gotten this far in either the stack trace or even just, like the user journey, as I know you like to branch early in your code.
JOËL: [laughs].
STEPHANIE: But you should also branch early from a user's experience [laughs].
JOËL: In general, I'm just a big fan of having a communication loop between development and product, not only sort of receiving a lot of useful information from the product team about what we want to build. But then because we're encountering this more, like, technical spec that we're writing, have those conversations bubble back to product and say, "Hey, so we talked about a dropdown where there are sort of three different ways of processing an application. Let's talk a little bit more about what it means to have three different ways of processing. And what do we want to name that? Is that accessible to everyone, or are they sort of one-to-one tied with a type of user?"
And all of a sudden, that has just generated probably a lot of questions that product never even thought to ask because they're working on an infinite canvas of possibilities. And it's really helped you as a developer to have better names to write your code and sort of better sketch out the boundaries of the problem you're trying to solve. So, I think it's a really healthy loop to have. I strongly encourage it.
So, we've spent a lot of time talking about thinking about behavior and things like the domain-driven design movement. But a few other things I want to shout out as being really helpful, one is an exercise where you take a problem statement and just underline all of the nouns. That is a great way to get a sense of, like, what is going on here.
More generally, I think a lot of what we're talking about falls under the umbrella of what you might call analysis. And so, digging into different analytic techniques can be a great way to better understand the problem that you're working through. One such tool would be decision tables. So, you have a problem, and you say, "Well, given these inputs, what should the outputs be?"
STEPHANIE: Cool. If there were any techniques or tools that we missed in terms of how you load code in your brain or generate code from your brain [laughs], we would love to know. You can write in to us at hosts@bikeshed.fm.
JOËL: On that note, shall we wrap up?
STEPHANIE: Let's wrap up.
STEPHANIE: Show notes for this episode can be found at bikeshed.fm.
JOËL: This show has been produced and edited by Mandy Moore.
STEPHANIE: If you enjoyed listening, one really easy way to support the show is to leave us a quick rating or even a review in iTunes. It really helps other folks find the show.
JOËL: If you have any feedback for this or any of our other episodes, you can reach us @_bikeshed, or you can reach me @joelquen on Twitter.
STEPHANIE: Or reach both of us at hosts@bikeshed.fm via email.
JOËL: Thanks so much for listening to The Bike Shed, and we'll see you next week.
ALL: Byeeeeeeee!!!!!!!
AD:
Did you know thoughtbot has a referral program? If you introduce us to someone looking for a design or development partner, we will compensate you if they decide to work with us.
More info on our website at: tbot.io/referral. Or you can email us at: referrals@thoughtbot.com with any questions.
439 episodes
All episodes
×Welcome to Player FM!
Player FM is scanning the web for high-quality podcasts for you to enjoy right now. It's the best podcast app and works on Android, iPhone, and the web. Signup to sync subscriptions across devices.