Speaker 1: How'd you like to listen to dot net rocks with
Speaker 1: no ads? Easy? Become a patron for just five dollars
Speaker 1: a month. You get access to a private RSS feed
Speaker 1: where all the shows have no ads. Twenty dollars a month,
Speaker 1: we'll get you that and a special dot net Rocks
Speaker 1: patron mug. Sign up now at Patreon dot dot NetRocks
Speaker 1: dot com. Hey, welcome back to dot net rocks. I'm
Speaker 1: Carl Franklin and I'm Richard Campbell. We are here for
Speaker 1: your dot net pleasure.
Speaker 2: And any other pleasures you want. But you know, don't
Speaker 2: get crazy.
Speaker 1: No, I'm gonna limit it to dot net Okay, maybe
Speaker 1: geek up pleasure, maybe a little science.
Speaker 2: Yeah, I'm writing my indie year geek outs. Man, it's
Speaker 2: a pile of work, but I am working on them.
Speaker 1: That's great. I can't wait to can't wait to hear them.
Speaker 2: Yeah.
Speaker 1: All right, Well let's get started with a little thing
Speaker 1: we call better no a framework.
Speaker 2: Awesome. All right.
Speaker 1: So I went looking to see what's trending as far
Speaker 1: as c sharp repositories on GitHub, and one that has
Speaker 1: recently got a lot of activity or a lot of
Speaker 1: coolness is Tom Hurst's t unit a modern, fast, and
Speaker 1: flexible dot net testing framework. Now I have never used it.
Speaker 1: I don't know anything about it except what it says
Speaker 1: in the repo, which is that it's modern and fast.
Speaker 1: T unit leverages source generators to locate and register your
Speaker 1: test as opposed to reflection. Nice you'll have a slight
Speaker 1: bump and build time, but a speedier runtime. T unit
Speaker 1: also builds upon the newer Microsoft Testing platform that's Microsoft
Speaker 1: Dot Testing dot Platform. Whereas most other frameworks you'll have
Speaker 1: to you'll use, we'll use vs tests. The new platform
Speaker 1: is reconstructed from the ground up to address pain points,
Speaker 1: be more extensible, and be faster.
Speaker 2: So this is just a play on x unit, but
Speaker 2: x unit depends on reflection, and here they're using analyzers.
Speaker 1: Yes, some sort.
Speaker 2: Of modernized cunts. It's sort of modernized. Yeah, that's good idea.
Speaker 2: I like that.
Speaker 1: Yeah. So, like I said, it's trending and you might
Speaker 1: want to check it out. That's when I got Richard,
Speaker 1: who's talking to us today.
Speaker 2: Grady comment to off a show nineteen sixteen. So that's
Speaker 2: the one we did with Rendell back in September. They
Speaker 2: show I called how Simple is as Simple as Possible?
Speaker 3: Right.
Speaker 2: I mean, Rendell's a smart person, right like, he just
Speaker 2: thinks about development really coherently and very typical of you know,
Speaker 2: how do we do the right thing the right way?
Speaker 2: And it kicked off a ton of comments, lots of
Speaker 2: people reacting to the show, and Cash Bonfil said this,
Speaker 2: he said, this is a really interesting show and if
Speaker 2: it's writing discussions we're currently having at work. We're a
Speaker 2: team of eight developers all together, so hardly the size
Speaker 2: of Netflix. That it's okay, man, you don't need to
Speaker 2: be this side of Netflix. Now, that's set of problems.
Speaker 2: We're building a lot of micro services because we can.
Speaker 2: It's all good, but it comes to the price. One
Speaker 2: example is that we are making everything asynchronous, that is,
Speaker 2: with service bus integrations in the form of topics and queues.
Speaker 2: Again all good on paper, because every time we have
Speaker 2: AC and communications between our very small services, we have
Speaker 2: situations where things don't work out as it was supposed to.
Speaker 2: Messages end up on dlques and someone or something has
Speaker 2: to monitor and compensate. All of a sudden, we start
Speaker 2: building endpoints and handles on how I allow messages to
Speaker 2: be fixed and rerun. So we're spending our precious time
Speaker 2: creating things to compensate for architectural and infrastructure things instead
Speaker 2: of actually building business value. My point is that on
Speaker 2: an architectural drawing, a simple straight line might end up
Speaker 2: being insanely complex. Another funny thing, we started using dapper
Speaker 2: that's DAPR for the exact reasons that Mendel mentions. Eventually
Speaker 2: we left it again and it turned out to have
Speaker 2: to be a horrible developer experience. Dapper is another great
Speaker 2: example of being a good idea from an architectural point
Speaker 2: of view, but implementing it it was a nightmare. I'm
Speaker 2: pretty sure it will improve in the future, but the
Speaker 2: moment it's too mature from a developer's point of view.
Speaker 2: Thanks again for the great show. I always feel like
Speaker 2: having a biscuit and a cup of tea when you
Speaker 2: have rent a lot.
Speaker 1: Absolutely or some fish and chips and a stout more.
Speaker 2: Like, and it's stout and some mushy peas for sure.
Speaker 1: Yeah, you can keep this.
Speaker 2: You know, it's interesting that you've gone down that path
Speaker 2: and you're seeing the struggles around all of it. And
Speaker 2: again it's like, because you can thing problem now is
Speaker 2: that d architecting would be an interesting challenge, although it
Speaker 2: shouldn't be that tough. You've already done the hard work
Speaker 2: separating concerns. Now you want to put some of them
Speaker 2: back together. But great to hear the experiments going on
Speaker 2: and the challenges there. And you're a pretty big team.
Speaker 2: I mean eight people is enough that that ability is split.
Speaker 2: The work up's pretty cool. So COJH, thank you so
Speaker 2: much for your comment, and a coffee of music code
Speaker 2: by is on its way to you. And if you'd
Speaker 2: like a copy of musiccode buy, I write a comment
Speaker 2: on the website at dot NetRocks dot com or on
Speaker 2: the Facebook, so we publish every show there, and if
Speaker 2: you comment there and I read it on the show,
Speaker 2: we'll send you a copy of music cope or.
Speaker 1: If you feel like buying music to code but how
Speaker 1: you can go to music tocode by dot net and
Speaker 1: you can also follow us on ex Twitter. Of course
Speaker 1: we've been there for years, but the cool kids are
Speaker 1: hanging out. I'm mastaed on, I'm at Carl Franklin at
Speaker 1: techeb dot social.
Speaker 2: And I'm rich Campbell at masadon dot social.
Speaker 1: Send us a two and uh, Richard. Before we get
Speaker 1: started here, I realized that I didn't do my you know,
Speaker 1: what happened in nineteen twenty three. This is show in
Speaker 1: nineteen twenty three, and we've sort of vowed to do that.
Speaker 2: Oh wow, yeah, what happened in nineteen twenty three.
Speaker 1: Well, I'm going through the Wikipedia thing because my usual
Speaker 1: page that we go to is down for some reason.
Speaker 1: But let's just pick a few here. January fifth, Lithuani
Speaker 1: begins the Clyppeeda revolt to annex the Clyipeda region. I
Speaker 1: don't know.
Speaker 2: Oh no, this is pre World War two, right, so
Speaker 2: you're talking about the fall that's sort of the fallout
Speaker 2: of the end of the Ottoman Empire, and it's a
Speaker 2: it's craziness for sure.
Speaker 1: March first, Eskom, the largest electricity producer in Africa, is
Speaker 1: established in South Africa. March third, the first issue of
Speaker 1: Time magazine is published.
Speaker 2: Wow, that's cool.
Speaker 1: March ninth, Vladimir Lenin suffers his third stroke, which renders
Speaker 1: him bedridden and unable to speak. Consequently, he retires from
Speaker 1: his position as chairman of the Soviet government. Let's see
Speaker 1: British Prime Minister in May Bonar Law resigns due to
Speaker 1: ill health. Okay, not all that oh, the Irish Civil
Speaker 1: War ended May twenty fourth. Let's see what else I
Speaker 1: can find here. July tenth, large hailstones killed twenty three
Speaker 1: people in Rostov and the Soviet Union.
Speaker 2: Oh my wow, those are big hailstones that they're killing people.
Speaker 1: Yeah. July twentieth, Pancho Villa is assassinated a Hidalgo de
Speaker 1: Palo Shiwawa. The Treaty of Lusine is signed, setting the
Speaker 1: boundaries of modern Republic of Turkey in Switzerland.
Speaker 2: Right, I would argue the big thing in twenty three
Speaker 2: is the beer hall push is that's the beginning of
Speaker 2: the first coup attempt by Hitler and his go court
Speaker 2: hearts that failed.
Speaker 1: Beer hall push, Beer hall pushed, Oh, pohar pusch. November eighth,
Speaker 1: in Munich, Adolf Hitler leads the Nazis and an unsuccessful
Speaker 1: attempt to overthrow the Bavarian government. Police and troops crushed
Speaker 1: the attempt. The next day. Twenty people die as a
Speaker 1: result of associated violence. And our friend Christian Vayer was
Speaker 1: not born yet.
Speaker 2: No, but it did, you know? And one would argue
Speaker 2: the lack of consequences for the party at that time
Speaker 2: set them up for what would happen later.
Speaker 1: Yeah, I don't know why you brought that up. Is
Speaker 1: there some sort of current event or something that you're
Speaker 1: referring to.
Speaker 2: I'm not referring to anything, not referring to a thing.
Speaker 2: I don't know what you're talking about.
Speaker 1: I don't know it's you're talking about either. Okay, all right,
Speaker 1: let's bring in our guest. Vlad Honanoff is a software
Speaker 1: architect with extensive industry experience, having held roles ranging from
Speaker 1: webmaster to chief architect. He currently helps organizations make sense
Speaker 1: of their business domains, untangled monoliths, and address complex architectural challenges.
Speaker 1: In twenty twenty one, Vlad published Learning Domain Driven Design,
Speaker 1: followed by his latest book, Balancing, Coupling and Software Design,
Speaker 1: released earlier this month. As a public speaker, Vlad presents
Speaker 1: at conferences worldwide on topics such as software architecture, demain
Speaker 1: driven design, and distributed systems. And I believe this is
Speaker 1: his first time on dot Netrock. So welcome, Flad.
Speaker 4: Thank you so much. It's such an honor been here.
Speaker 1: Oh you've heard of our show?
Speaker 4: Then, yeah, bit, I started listening to you in two
Speaker 4: thousand and six, I believe, Wow, and then use Yeah,
Speaker 4: spoiled experience of listening to other podcasts for me because
Speaker 4: that's the quality I expected from the rest of them.
Speaker 1: Yeah, we get that a lot.
Speaker 2: We've always taken the engineering side of the show very seriously. Yeah,
Speaker 2: the content we have more fun, but quality audio is important.
Speaker 1: Our content quality went way up when Richard joined the team,
Speaker 1: that's for sure.
Speaker 2: That's a long time ago. Man, it's coming up on
Speaker 2: twenty years, like in a few months.
Speaker 1: Yep, yep. And remember that serious lapse year that we
Speaker 1: had before you came on, where we started doing comedy
Speaker 1: and to two hour shows and what were we thinking.
Speaker 2: Two and a half? Yeah, it was a variety showed.
Speaker 2: I remember talking about it. It's like there's a vent diagram here.
Speaker 2: There's a vent diagram of folks who want a technical
Speaker 2: interview and folks who want like music and geeky humor
Speaker 2: and all those things, and the intersection is small, but
Speaker 2: the two big other circles are big. Why don't we
Speaker 2: just pull them up? Part that's right, So Mondays was born,
Speaker 2: both shows got bigger.
Speaker 1: Okay, lad, enough about us. Let's talk about your experience
Speaker 1: untangling monoliths and getting the balance of complexity right. What
Speaker 1: are you talking about.
Speaker 4: Yeah, so actually the comment that Richard read that experience
Speaker 4: sounded kind of similar to what I went through a
Speaker 4: few years ago. We also had a system that was working,
Speaker 4: that was producing business value, but it was a monolith.
Speaker 4: It was a monolithic code base. So we said, okay,
Speaker 4: let's make it better by breaking it down into micro
Speaker 4: services or in other words, let's decouple things. Right, So
Speaker 4: we introduced tons of services that we call the micro services,
Speaker 4: of course, with asynchronous integration between them, and from that
Speaker 4: point on, it was impossible to evolve that system. It
Speaker 4: was impossible to change it because suddenly we had to
Speaker 4: modify tons of those services or I don't want to
Speaker 4: say microservices because they have nothing to do with micro services.
Speaker 2: Yeah, I don't know how. I mean, how do you
Speaker 2: call it. It's only a micro service if you put
Speaker 2: that in the comments, like, I don't know the difference.
Speaker 2: It's just a service. Yeah.
Speaker 1: The thing is they're completely separated, right. But I guess
Speaker 1: what we're talking about, and what we've been talking about
Speaker 1: the complexity aspect of it is when you want to
Speaker 1: implement a feature, you have to touch so many different
Speaker 1: places to make sure that it is still going to
Speaker 1: be robust.
Speaker 4: Yeah. Yeah, and again it started with that decoupling effort
Speaker 4: by breaking things apart. But what we did is we
Speaker 4: increased coupling. Essentially, Now everything was coupled. And if previously
Speaker 4: we had to modify one code base, now we had
Speaker 4: to modify multiple projects and then synchronize changes, synchronize their
Speaker 4: life cycles. Tons of fun. And yeah, it was a
Speaker 4: failure atostrophic failure.
Speaker 2: I mean the other the alternative there is that you
Speaker 2: run multiple versions of each service. I've gone down that
Speaker 2: path where it's like, hey, we're you know, if we
Speaker 2: change this API, we're going to affect these other things.
Speaker 2: It's like, okay, well let's keep this old one running,
Speaker 2: but we'll run a new one as well, and let's.
Speaker 1: Have a service that returns the current versions of all
Speaker 1: the services that you should be running.
Speaker 2: That wasn't a problem at all. We were fine, Everything
Speaker 2: was fine. That's fine.
Speaker 1: If you had a merge conflict in that service, the
Speaker 1: version service service service version service, oh my god, nightmare.
Speaker 2: But you know, I do feel like you're trading plumbing
Speaker 2: issues off for monolith issues. Nothing comes for free here. Yeah, absolutely,
Speaker 2: where's the balance in this? Because I know it's in
Speaker 2: the title.
Speaker 4: Yeah. So, following that experience, that catastrophic failure of that project,
Speaker 4: I wanted to figure out what microservices are, how to
Speaker 4: design their boundaries properly. And I found the answers I
Speaker 4: was looking for in a book that was published in
Speaker 4: early seventies. It's called Structure Design, and the answers were
Speaker 4: in chapter on coupling, and in it I found some
Speaker 4: interesting things that our industry knew in the past, but
Speaker 4: we kind of have forgotten it now. So what I did,
Speaker 4: what I wanted to do, is to adapt those forgotten
Speaker 4: insights to our current technological world, the technologies we are
Speaker 4: working on today, and that's how the balanced coupling model
Speaker 4: was born. The idea here is that whenever you have
Speaker 4: two components, and these components can be anything, whether these
Speaker 4: are services, microservices, or two classes interacting with each other,
Speaker 4: in order to work together, they have to exchange some
Speaker 4: knowledge about each other, like what is your public interface,
Speaker 4: what is the format of the data I have to pass?
Speaker 4: Or maybe what are the assumptions I have to make
Speaker 4: about your behavior? So let's call that knowledge. The second
Speaker 4: aspect is the distance between them. Now, if we're talking
Speaker 4: about two classes, then their source code is located close
Speaker 4: to each other, those are probably the same files and
Speaker 4: the same directory.
Speaker 2: Sure. On the other hand, in the same language.
Speaker 1: Yeah.
Speaker 4: On the other hand, if we're looking at two micro services,
Speaker 4: then we have much greater distance between those source codes. Right,
Speaker 4: greater distance. Now, why we should be interested in it
Speaker 4: is the greater the distance, the more collaboration and more
Speaker 4: communication effort you need to implement a change that affects
Speaker 4: both of them. So I'm going back to that example
Speaker 4: from the comment. Once you decompose a monelith into micro services,
Speaker 4: you're increasing the distance between those parts of the system.
Speaker 4: Those components. Now they're residing in different micro services, so
Speaker 4: we have greater distance. And when we look at these
Speaker 4: two the mansions, on the one hand, we have the
Speaker 4: knowledge that they have to share, and on the other
Speaker 4: hand we have distance between them. Then we can deduce
Speaker 4: some interesting insights by evaluating their extreme extreme combinations. For example,
Speaker 4: if you have a lot of knowledge change across a
Speaker 4: big distance, well, that's not going to be foul.
Speaker 1: Right.
Speaker 4: The more knowledge they share, the more cascading changes are
Speaker 4: going to follow, Like the more reasons to change together
Speaker 4: those components have. Now, when you couple that with the
Speaker 4: increased distance. Then we have lots of skating changes, and
Speaker 4: we have lots of effort needed to implement each of
Speaker 4: those changes. Bottom line, not fun. Or we can look
Speaker 4: at the opposite of that. What if we have two
Speaker 4: components that are not sharing much knowledge, maybe they are
Speaker 4: not even integrated. They're just located close to each other,
Speaker 4: so we have low knowledge and low distance between them.
Speaker 4: Now what we have is components that are not truly
Speaker 4: related to each other, located in the same code, based
Speaker 4: the same names, paid the same package module, however you
Speaker 4: call it. That's what we usually call the classic modelife
Speaker 4: or a big ball of mod You have a bunch
Speaker 4: of stuff that is not related, but it's close to
Speaker 4: each other, and the next time you have to modify something,
Speaker 4: you have to make your ways through those unrelated things
Speaker 4: to find what is that class or module that you
Speaker 4: do have to modify. And both these relationships high distance,
Speaker 4: high strength, high knowledge. I call that dimension of knowledge
Speaker 4: sharing integration strength. So once you have high integration strength
Speaker 4: and high distance, you have complexity on the overarching system.
Speaker 4: If you have low knowledge and low distance, then suddenly
Speaker 4: you have another type of complexity. You have cognitive load
Speaker 4: that results from you having to struggle to understand what's
Speaker 4: going on, what are those things that are implemented in
Speaker 4: the same module, and what do you have to modify
Speaker 4: when a new business requirement comes in. So those are
Speaker 4: two types of complexities. Now, what if we reverse these relationships.
Speaker 4: What if we will look at low integration strength, which
Speaker 4: means low amount of knowledge shared across a big distance. Well,
Speaker 4: big distance means that now we still have to to
Speaker 4: invest high effort in implementing cascating changes. On the other hand,
Speaker 4: we have that low integgression strength, So there we are
Speaker 4: minimizing the chances of those cascading chances a changes happening.
Speaker 4: And that relationship is usually what we call a loose coupling.
Speaker 2: Right, and it's what happens most of the time, right
Speaker 2: when you're calling in the third party APIs and things
Speaker 2: like that. They're all loosely coupled because they are low
Speaker 2: distance with low levels of integration. Right, hopefully for long
Speaker 2: distance with low levels of integration, they're not inside your perimeter.
Speaker 2: They have skate context. Yeah, you know, you've got to
Speaker 2: carry your own state like you're not counting on anything,
Speaker 2: You're they're external to you.
Speaker 1: Effect ask a question, get an answer.
Speaker 4: Yeah, And the opposite of that is going to be high.
Speaker 4: I'm out of an aggression strength high amount of knowledge over.
Speaker 2: Small distance right now local.
Speaker 1: Yeah.
Speaker 4: Now, we do want to minimize the knowledge we are
Speaker 4: sharing across the boundaries of components, of course, but it's
Speaker 4: not always possible. Sometimes we do have to share knowledge
Speaker 4: because maybe we have two components that are implementing closely
Speaker 4: related business functionalities, so we have to share knowledge.
Speaker 1: Yeah, maybe they're both injecting the same services, that kind
Speaker 1: of thing.
Speaker 4: Yeah, Yeah, there are different levels of evaluating that knowledge.
Speaker 4: So let's assume we're on that higher end of the scale,
Speaker 4: and there is no way for us to reduce the
Speaker 4: amount of knowledge in that case by putting them closer
Speaker 4: to each other. We know, we acknowledge that. Yeah, they
Speaker 4: will change together, but we are preparing ourselves by minimizing reasy.
Speaker 2: Easy to do that. Yeah, So if I mean I
Speaker 2: see the rule emerging here, then this, Hey, these two
Speaker 2: things are to be closely tied together, we should make
Speaker 2: sure they're close. Yeah, yeah, yeah, and that. In nineties
Speaker 2: a model was formulated called connaisance, and in that model,
Speaker 2: its author Miller Page Jones use that word proximity to
Speaker 2: say that things should be in close proximity to each other.
Speaker 2: I prefer the world distance, which is the opposite of
Speaker 2: proximity for other reasons.
Speaker 1: Yeah.
Speaker 4: Yeah, but that's so.
Speaker 1: The basic idea is that go ahead and have your
Speaker 1: micro services or your services or whatever, but those that
Speaker 1: share knowledge should be closer together, and the ones that
Speaker 1: should be have more distance, should have less dependency on
Speaker 1: each other. Yeah, share share less.
Speaker 4: Knowledge, Yeah exactly, Yeah, and that makes sense. The next
Speaker 4: question is how do you evaluate that knowledge?
Speaker 1: Right?
Speaker 4: Yeah, And that's the complicated part, because how do you
Speaker 4: measure knowledge? Like what is the management measurement unit for knowledge? Yeah?
Speaker 2: But how much do I need to know?
Speaker 4: Yeah? Yeah, And for that I adapted that model from
Speaker 4: seventy's book Structure Design to differentiate between a number of
Speaker 4: types of knowledge knowledges. Now, once we're dealing with types,
Speaker 4: we don't have to measure the exact amount of knowledge,
Speaker 4: because once we're going higher on that scale of types,
Speaker 4: the amount is going to be significantly higher anyway. So
Speaker 4: overall there are four levels, four basic levels. One of
Speaker 4: them is, let's say if we are implementing two components.
Speaker 4: Let's say Richard, you're implementing micro service, which means when
Speaker 4: I'm integrating with it, I should be using its public interface,
Speaker 4: but I'm not going to do it. Instead, I will
Speaker 4: reach out to your database directly and take whatever the
Speaker 4: data I need.
Speaker 1: Yeah, it.
Speaker 2: Memories make the batman stop.
Speaker 4: Yeah. So what I'm doing here is I'm introducing a
Speaker 4: dependency on your implementation detail.
Speaker 2: Right, and I can break you very easily by modifying
Speaker 2: my database for my next version.
Speaker 4: Exactly.
Speaker 2: I not know that I did exactly. Yeah.
Speaker 4: That's why let's call this level intrusive coupling. I am
Speaker 4: intruding your boundaries.
Speaker 2: I mean in a way that doesn't bother me all
Speaker 2: that much, because you're I can break you. You can't
Speaker 2: really break me.
Speaker 4: It depends what I'm doing there.
Speaker 2: It's kind of like the beer puts. Yeah, I mean
Speaker 2: you can't. You're right, you can by inserting weird data
Speaker 2: into my database. Yeah, that you know, can break my
Speaker 2: code expecting certain consistency in the database, which is my fault.
Speaker 2: I should have had put parameters on my database that
Speaker 2: don't allow you to do that.
Speaker 4: Yeah. So that's intrusive coupling. That's the highest level of
Speaker 4: knowledge sharing. We're assuming that this is the knowledge you
Speaker 4: shared it.
Speaker 2: Yeah, but this seems like I don't do this, this
Speaker 2: is bad.
Speaker 1: Yeah, this is only.
Speaker 4: Level one depends let's get back to it.
Speaker 2: Okay, let's get back to it.
Speaker 4: The next level is functional coupling, and here we have
Speaker 4: two components that are implementing closely related functionalities. Maybe they're
Speaker 4: implementing closely related use cases, but the bottom line is
Speaker 4: the wheel have to change together when the business requirements.
Speaker 2: Change, so they resonate together.
Speaker 1: Yeah.
Speaker 4: Yeah, So they have to share lots of knowledge because
Speaker 4: once that functional dependency is there, we have to make
Speaker 4: an assumption that there will be business rules that both
Speaker 4: of them will have to follow. So that's the functional coupling. Now,
Speaker 4: there are different degrees of functional coupling. Let's not get
Speaker 4: into it because we'll need another hour just for that
Speaker 4: sure topic.
Speaker 2: But is it one of the arguments that you should
Speaker 2: have been the same service.
Speaker 4: Yes, but let's get back to it later.
Speaker 2: Okay, I'm jumping head.
Speaker 1: Yeah.
Speaker 4: The third level is model coupling. This one means that
Speaker 4: we have two components that share the knowledge of the
Speaker 4: model of the business domain that are using Now, every
Speaker 4: time I talk about this level, I have to wear
Speaker 4: my dominion and design guide CAP because it's heavily influenced
Speaker 4: by the Clean Dream design. And the thing is, when
Speaker 4: we are implementing a system, of course, we are not
Speaker 4: coding all the knowledge available about that business domain, but
Speaker 4: we have to pick whatever we think is relevant to
Speaker 4: solve the problem that we want to solve for the
Speaker 4: users of our systems. Right, So we're crafting a model
Speaker 4: of a business domain now with new requirements, with new
Speaker 4: insights into business domain. That model should evolve over time.
Speaker 4: Once we're sharing that knowledge, we kind of reducing our
Speaker 4: ability to evolve that model, so it can be problematic
Speaker 4: in some cases. That's the third level of integration strengths.
Speaker 2: But models don't change as often as functionality does.
Speaker 4: Well, it depends what kind of system you're working on.
Speaker 4: Let's say that you are working for a star up
Speaker 4: company and at the first stages there's so much uncertainty
Speaker 4: about twitching. Yeah, everything about how we're going to solve
Speaker 4: the problem for our customers, and even the problem itself
Speaker 4: can change. Yeah, So that's model coupling. The third level, Now,
Speaker 4: the lowest one is what I call contract coupling. And
Speaker 4: here we have an integration specific contract that was formulated
Speaker 4: for integration purposes only. Its goal is to encapsulate all
Speaker 4: the knowledge about implementation details, all knowledge about functional requirements,
Speaker 4: and of course all the knowledge about the model of
Speaker 4: the business domain that's being used to implement the component.
Speaker 4: What we want to expose is a much more stable
Speaker 4: interface that is not going to be changed after each
Speaker 4: new business requirement that comes in something way more stable.
Speaker 2: This is that public API inter face mindset exactly.
Speaker 4: Yeah, yeah, yeah. And if I will bring back my
Speaker 4: domain gveing design cap again, then have anti corruption layer,
Speaker 4: we have open host service. These parents. What they're doing
Speaker 4: essentially is introducing an integration contract for those purposes. So
Speaker 4: we have four types of interfaces, contract, model, functional, and interesting.
Speaker 4: These are four types of knowledges, and we cannot put
Speaker 4: a number to evaluate the amount of knowledge we are sharing.
Speaker 4: But knowing these four types already helps us to evaluate
Speaker 4: where we are.
Speaker 2: Yeah, at least get people in the categories, right, yeah.
Speaker 4: Now, as I mentioned in the book, there are degrees
Speaker 4: for functional model and contract coupling, so that you can
Speaker 4: compare to interfaces of the same type. These degrees are
Speaker 4: based on the conations model. It has lots of levels,
Speaker 4: so it will require another hour.
Speaker 2: Just that topic, by the book is a good number, right,
Speaker 2: Like you could probably discribe, especially at the functional level,
Speaker 2: you could discriminate a bunch of options in there. But
Speaker 2: I'm with you. Four you can feel good about. It's like,
Speaker 2: I'm not just thinking that all coupling is bad. I'm
Speaker 2: recognizing some is necessary, but some has larger consequences than others.
Speaker 4: Yeah. Now, let's talk about the dimension of space, the
Speaker 4: distance between those components. So on the one hand, we
Speaker 4: have the code base where the components are implemented. If
Speaker 4: the files are in the same directory, the distance is low,
Speaker 4: or maybe in the same file even the distance is
Speaker 4: minimized versus let's say, different project, different repositories. But there
Speaker 4: is another aspect to that dimension, and it's organizational. First.
Speaker 4: Let's say that we have two micro services, and we
Speaker 4: have two cases. In the first one, both micro services
Speaker 4: are implemented by the same team. In the second one,
Speaker 4: we have two micro services implemented by different teams. In
Speaker 4: the second case, we'll need much higher collaboration and communication
Speaker 4: effort to implement a change. So it also affects the distance.
Speaker 2: I mean, I argue it's the same amount of communication
Speaker 2: it's just one is easy because you're in the same team,
Speaker 2: and one needs more structure because you're not the same.
Speaker 4: Yeah, exactly, So we have that organizational aspect that increases
Speaker 4: the distance. And also again going back to the comment
Speaker 4: that you read in the beginning of the show, Let's
Speaker 4: say we have two components that are imp implementing synchrons
Speaker 4: communication between them, and the second case with asynchronous communication
Speaker 4: between them. Right now, in the case of asynchronus communication,
Speaker 4: we have a bigger distance between them, bigger virtual distance
Speaker 4: between them. Let's say that way because we have a
Speaker 4: lower life cycle dependency between them.
Speaker 2: Right.
Speaker 4: If we have low distance, let's say components implemented in
Speaker 4: the same monolith, then we have that life cycle dependency
Speaker 4: between those services. Everything within that MAELD have to be implemented, tested,
Speaker 4: and deployed.
Speaker 2: Simultaneously, functionally, but coupled.
Speaker 1: Yeah.
Speaker 4: Once we were introducing that asynchronous communication between them, we
Speaker 4: are kind of extending the distance, right.
Speaker 1: Yeah.
Speaker 4: Now, let's go back to the topic of ineggression strength.
Speaker 4: Let's say that we have two components share lots of
Speaker 4: knowledge between them. Let's say they have interessive coupling between them.
Speaker 4: The bad one M and let's assume we have high
Speaker 4: distance between them. Is this design necessarily bad?
Speaker 2: I mean, it's got a lot of consequences to it. Yeah, right,
Speaker 2: and you need a lot of communicating to move you're
Speaker 2: often going to have if you've got a good testing harness,
Speaker 2: it's going to fail a lot because you're moving things
Speaker 2: that affect the other composers.
Speaker 1: I would ask, why did you choose this model? Oh,
Speaker 1: why do you need that?
Speaker 4: That's a great question, So I'll provide you more information
Speaker 4: about the context. So here's the thing. I work on
Speaker 4: a system that integrates with a legacy system that is there.
Speaker 4: This is not going to change ever. Nobody wants to
Speaker 4: touch it, so there's no one I can ask to
Speaker 4: add new interface to it so that I can work with.
Speaker 2: You're not going to get any changes.
Speaker 1: Got to go directly to the database, that's what you're saying.
Speaker 4: Yeah, so I will do it. But I made that
Speaker 4: assumption that that legacy system is not going to change,
Speaker 4: right right, Okay, And that's the third dimension of coupling volatility.
Speaker 1: Hey, I think this feels like a good place to
Speaker 1: take a break, So hold that thought. Flag will come
Speaker 1: right back after these very important messages don't go away.
Speaker 1: Did you know that you can work with AWS directly
Speaker 1: from your ID. AWS provides toolkits for visual studio, visual studio, code,
Speaker 1: and jet brains rider. Learn more at AWS dot Amazon
Speaker 1: dot com, slash net slash tools, and we're back. It's
Speaker 1: dot net rocks. I'm Carl Franklin, that's Richard Hey Campbell,
Speaker 1: and we're talking to Vlatkonanoff about you know, decoupling, coupled, balancing, decoupling,
Speaker 1: I think is what we're calling it.
Speaker 2: Yeah, balancing coupling.
Speaker 1: Yeah, balancing coupling or dec degree.
Speaker 2: So you just hit a third dimension there, the volatility
Speaker 2: versus the distance, which is not necessarily a physical distance.
Speaker 2: It's also are the teams separated or are they on
Speaker 2: disparate teams? Might be security boundaries. There's a bunch of
Speaker 2: different ways with great separation. And then the level of
Speaker 2: interaction or the coupling within the code, how tightly tied
Speaker 2: together they are, Yeah, alas sharing, so yeah, they manage
Speaker 2: knowledge sharing and the bund scenario you painted there where
Speaker 2: it's like, Okay, I'm doing some intrusive coupling on it
Speaker 2: in my new app against its existing app, which is
Speaker 2: relatively nearby. We're working in the same context, but its
Speaker 2: volatility is very low, as in, I'm one of the
Speaker 2: reasons I'm doing this intrusive behavior is because I cannot
Speaker 2: change the other phase. So that doesn't seem to me
Speaker 2: like the lowest thing. It seems like the only solution circumstance.
Speaker 4: Yeah, and that brings us to the title of that
Speaker 4: model balanced coupling. Once we have those three dimensions, once
Speaker 4: we can evaluate them, we can say, okay, so we
Speaker 4: have in aggression strength and we have distance, and we
Speaker 4: have to be inversely proportional to each other. Okay, okay,
Speaker 4: once one of them gets higher, the second one should
Speaker 4: get lower. We can even find some middle ground. We
Speaker 4: have middle amount of medium amount of knowledge, then medium
Speaker 4: distance is okay too. But sometimes we're needed. We can
Speaker 4: be a bit more pragmatic by introducing that third dimension
Speaker 4: of volatility. And if components are not expected to change,
Speaker 4: those components that are sharing the knowledge, then it can
Speaker 4: be okay to allow them to share high amounts of
Speaker 4: knowledge across big distances.
Speaker 2: Right. Yeah, and well, and again you get into that
Speaker 2: motion of hey, we're finding a lot of coupling between this.
Speaker 2: Maybe we need to get this into the same team,
Speaker 2: like a lawful lot of complexity would end if we
Speaker 2: move that team member on that other team over with
Speaker 2: us while this work is going on, and then communication
Speaker 2: becomes easier. We can get through this period and decide
Speaker 2: what happens from there, as opposed to, Hey, this is
Speaker 2: complicated and it's distant, and we can't do anything about
Speaker 2: that part, so let's minimize the volatility.
Speaker 4: Yeah, And that's a very interesting comment because in the
Speaker 4: book I'm talking about software design. However, we had a
Speaker 4: conversation with a friend of mine, a Sonya and Tanzem
Speaker 4: about the organizational design, and turns out we can use
Speaker 4: the same model to evaluate the design of the organization.
Speaker 4: Like if you have people in the same team who
Speaker 4: are working on underlated things, which means they're not sharing
Speaker 4: much knowledge between them, right, why do we need them
Speaker 4: in one team?
Speaker 2: Why? Yeah? Why are in the same team?
Speaker 1: Yeah?
Speaker 2: And the opposite you do you talk about Conway's law,
Speaker 2: this idea that the team architecture also tends to dictate
Speaker 2: the software architecture effectively, like if you if you have
Speaker 2: three teams, you're going to make a three pass parser. Yeah,
Speaker 2: that's just how it's going to happen, and now you're
Speaker 2: talking about, Hey, as we get to the best architecture
Speaker 2: on this, we could start shaping the team structure so
Speaker 2: that Tonway's law doesn't fight us.
Speaker 1: Yeah.
Speaker 4: Absolutely, I think.
Speaker 2: That's really interesting. And you've described a scenario now where
Speaker 2: you might want to add a team member or even
Speaker 2: separate a team. Both scenarios are reasonable, which is probably
Speaker 2: a fairly good case. Then it's like, in both cases
Speaker 2: you can see why you would change the organization to
Speaker 2: reflect that intended art.
Speaker 1: First we fix the architecture, then we fix the team architecture.
Speaker 2: Perhaps, Yeah, I mean I think there's going to be
Speaker 2: an oscillation back and forth, Like we don't always see
Speaker 2: that these two things are going to end up coupled,
Speaker 2: right as that as they requirements e merge and you
Speaker 2: start seeing this is more and more and more coupled,
Speaker 2: you might have as anario where it's like, yeah, okay,
Speaker 2: we should probably bring these things together, but this participant
Speaker 2: can't necessarily join that team in easily. Should we change
Speaker 2: the team member into somebody you know, switch who's going
Speaker 2: to work on that into someone in that other team,
Speaker 2: because it'll make all of these other things easier, Like, yeah,
Speaker 2: that's an overhead too to change team members like that,
Speaker 2: but it might be less expensive than continuing in the
Speaker 2: current the current layout.
Speaker 4: Yeah, And as they say, organizational design always wins, right,
Speaker 4: So we need that that way of evaluating the type
Speaker 4: of knowledge that is being shared, whether it's across technical
Speaker 4: components or across teams or team members.
Speaker 1: Yeah, it's easier to change code than people.
Speaker 2: But sometimes it's more efficient to change the people if
Speaker 2: you can, Yeah, if you can. Like what I like
Speaker 2: is we've created a reason to have that conversation. Yes, Like,
Speaker 2: when I think in terms of balance company and balancing
Speaker 2: these three numbers, one of the options suddenly becomes do
Speaker 2: we change the team to compensate with this problem? Is
Speaker 2: that even a possibility, because sometimes that'd be a very
Speaker 2: easy solve, Like that person's always wanted to be on
Speaker 2: that team, and now we have a good reason to
Speaker 2: put them there, at least introducing that factor, like the
Speaker 2: idea that we're going to use conways a lot in
Speaker 2: our favor, and we're going to shuffle the team to
Speaker 2: increase the quality of software. That's slick, man, that's good thinking.
Speaker 1: It's been my experience, Richard, maybe years two and laid
Speaker 1: for that matter, that it's easier to add people to
Speaker 1: a team than to remove them, sure enough from a team,
Speaker 1: and people don't like being removed.
Speaker 2: I find depends on the person. But also like you
Speaker 2: can work into more independent you don't need to be here.
Speaker 1: Yeah, there's another team we want you to be on
Speaker 1: because it'd be more effective there, and that kind of thing.
Speaker 2: Yeah, this is the personality thing. But I just liked
Speaker 2: that you could put it on the table now in
Speaker 2: a coherent way. It's not political. I'm not after that
Speaker 2: person or anything like that. I see an issue in
Speaker 2: our architecture that is going to lead to consequences, and
Speaker 2: maybe a change of team would mitigate that.
Speaker 1: Interesting.
Speaker 2: Yeah, I know, I'm shaking up. I'm excited. Thanks, Flat,
Speaker 2: that's really interesting. That's a great way to talk about this.
Speaker 1: Cool.
Speaker 4: Yeah, it's important to have a framework to formulate these
Speaker 4: kind of things. But yeah, you know, when I decided
Speaker 4: I want to be a programmer, they told me that
Speaker 4: you're going to work with computers, not with people. Biggest
Speaker 4: lie of the century.
Speaker 2: Big lie totally turned out. People use the software and
Speaker 2: it ruins everything.
Speaker 1: I know, if you just get rid of the people,
Speaker 1: chopped so much.
Speaker 2: Yeah, you know, as long as nobody uses software, it
Speaker 2: works great.
Speaker 1: I wonder why. That's why you have front end developers
Speaker 1: who like people and back end developers who don't like people.
Speaker 1: You know, people become DBAs because they just want to
Speaker 1: talk to the database. That's it.
Speaker 2: But I do have to talk to people. I get
Speaker 2: to say no, no, no, you can't, no, you can't.
Speaker 2: I'm sorry, we're digressing. There's more to this story. I
Speaker 2: think you need to continue about it.
Speaker 1: But you know, you open a Pandora's box though. If
Speaker 1: you know architecture for both the teams and the code
Speaker 1: has to work hand in hand. It's definitely Conway's law
Speaker 1: applies here.
Speaker 4: Yeah, yeah, absolutely absolutely, And the two are interconnected. And
Speaker 4: that's what I love about that dimensional distance is that
Speaker 4: it reflects it. It reflects organizational desig even though you're
Speaker 4: looking just at the code.
Speaker 1: So you're a consultant. You go into a company and
Speaker 1: they have some horrible monolithic thing, or maybe they've got
Speaker 1: micro services whatever, But you see the problems that you've
Speaker 1: outlined here, and how do you go about convincing them
Speaker 1: about this kind of stuff without sounding too academic? Right?
Speaker 1: I mean, this is pretty technical stuff you're getting into here, right,
Speaker 1: You've got formulas and stuff, and maybe you have some
Speaker 1: stories from your experiences dealing with customers and how they've
Speaker 1: worked around it.
Speaker 4: Yeah. So the most common story is one of a
Speaker 4: company that wanted micro services and ended up with distributed monelith,
Speaker 4: and the hardest thing is to convince them to bring
Speaker 4: back everything into the same code base, to redesign it
Speaker 4: over time into a modular monelith, and then rebreak it
Speaker 4: into micro services.
Speaker 2: Interesting. Yeah, that doesn't seem like the right path, but
Speaker 2: I can see why it would be right.
Speaker 4: Yeah, because once you have it in the same code base,
Speaker 4: you have shorter distance, which means it's easier to change,
Speaker 4: to change things together.
Speaker 1: If you need to. Productivity goes up. Yeah.
Speaker 4: Now you re evaluate the boundaries of those services micro
Speaker 4: services that you want to build, and once they have
Speaker 4: that logical form of modules within the same model, if
Speaker 4: it's okay to be wrong, that's right, a mistake that
Speaker 4: is really easy to fix.
Speaker 2: But you're also doing this very empirically. You know, if
Speaker 2: you assemble them, they'll gather some momentum and life will
Speaker 2: be easier for a while, but eventually you're going to
Speaker 2: have a production issue of some kind that sort of
Speaker 2: points to, here's a piece of code that's resonating and
Speaker 2: if it were separated, it would cause less residence.
Speaker 4: That's true. Yeah, that's true, But there are different ways
Speaker 4: to There was a company I was working with. They
Speaker 4: had that code base which was monolithic, but it was
Speaker 4: still deployed as different services for that reason, not sually.
Speaker 4: For that reason, they needed different levels of scale for
Speaker 4: different components of that system. So the underlying code base
Speaker 4: is monolithic. Whatever there is change like new deployment, we
Speaker 4: already deploying all the services together like no exceptions, but
Speaker 4: still were able to gain the benefits of a lower
Speaker 4: cost of a design mistake and that advantage of kind
Speaker 4: of reducing the operational the cost of operational failure.
Speaker 1: Give me a story of where you found difficult people
Speaker 1: who dug in their heels and said, you know, I
Speaker 1: separated out these micro services because I read such and
Speaker 1: such a book and you know this is sound architecture
Speaker 1: and you don't know what you're talking about, and you know,
Speaker 1: meanwhile it's really slow and brittle. I mean, how do
Speaker 1: you deal with people like that?
Speaker 4: So usually when they call me, these people have already
Speaker 4: left the company. Ah really, well, maybe they were promoted
Speaker 4: so that they.
Speaker 1: Yeah, Gilbert Principle failing upward and moved to management.
Speaker 2: There you go.
Speaker 1: Yeah, I see, but you you have obviously at some
Speaker 1: point experienced and pushback, right of course. Yeah.
Speaker 2: But at the same time, they're not calling you because
Speaker 2: things are going well.
Speaker 1: Yeah.
Speaker 2: Yeah, they never called me for that. It's like, hey,
Speaker 2: I wanted to pay your fee and bring it in
Speaker 2: for a week just to show you how well everything's going.
Speaker 1: I think about Robert Irvine and restaurants impossible. How he
Speaker 1: goes into these failing restaurants and they dig in their
Speaker 1: heels and says, my food is great, and it's like, yeah,
Speaker 1: I can tell your one customer loves it.
Speaker 4: Well. You know what, if we're talking about pushbacks, then
Speaker 4: I'll bring back the topic of domain dreaming design because
Speaker 4: for some reason, the main driving design it's got this
Speaker 4: reputation of being too complex, with too much, too many
Speaker 4: patterns and if you apply them.
Speaker 1: Driven because it makes your foot look big and it's
Speaker 1: a it's a pistol, you know.
Speaker 4: Yeah. Now, once you face that kind of pushback, what
Speaker 4: I usually do is I started talking about the principles
Speaker 4: of domain dreaming design without mentioning domaindreamine design without mentioning
Speaker 4: concrete patterns like Okay, so we have this problem that
Speaker 4: we have to address. So that's the root cause of
Speaker 4: the issues you're facing. Now, maybe you want a brainstorm
Speaker 4: what we can do. And there was a guy that
Speaker 4: invented the aggregate pattern with a different name, but he
Speaker 4: was so happy, and I said, okay, cool, let's go
Speaker 4: on with it. Call it what a you wanted.
Speaker 1: Yeah, yeah, yeah, Bob's pattern.
Speaker 2: But yeah, you know it's people have their biases. So
Speaker 2: if you leave the words out but still do the
Speaker 2: work and get results. And maybe he retroduced the word later,
Speaker 2: but it's not even important. The goal was to get solved,
Speaker 2: so just find a way to get forward. The principles
Speaker 2: are still their useful principles for some reason.
Speaker 4: I don't know why, but this model of balanced coupling
Speaker 4: is way easier for people to grasp and to start
Speaker 4: using them. Domain dum in design, Yeah, I mean always
Speaker 4: compared with do major in design.
Speaker 1: I'm getting that. Yeah, I'm getting that. Four levels. That's
Speaker 1: all you need to know.
Speaker 2: Yeah, just that conversation of sorting your problems into these
Speaker 2: different categories and then saying and then you have it
Speaker 2: approach strategy for each one. It's different.
Speaker 4: Yeah, and if you look into pretty much almost all
Speaker 4: patterns from domain dreaming design, they have that balancing behind
Speaker 4: the scenes. If you have, for example, Eric Evans always
Speaker 4: says that not all of our system is going to
Speaker 4: be well designed. It means that supporting subdomains are not
Speaker 4: that important. It's okay to around some corners. What about
Speaker 4: supporting subdomains. They're not business critical, so they're not going
Speaker 4: to change that much, which means their volatility is going
Speaker 4: to be much lower than core subdomins. So again we're
Speaker 4: getting that balance. We can introduce some complexity because we
Speaker 4: have lower volatility, but when we're talking about high volatility
Speaker 4: of course subdomains, then yeah, we should keep things as
Speaker 4: well designed as possible. Sure we have those patterns for
Speaker 4: encapsulating models in bounded contexts, etc.
Speaker 2: You're also learning to pick your fights right. You get
Speaker 2: people together that be architecturally absolutist in everything has to
Speaker 2: be built the same way, where by going through this
Speaker 2: coupling practice and figuring out what's actually going to be problematic,
Speaker 2: only fight for the ones that are going to have
Speaker 2: the biggest harm if they are done poorly. The edge
Speaker 2: cases can be a little can be weaker because the
Speaker 2: consequences are small. But where the consequences are big, then
Speaker 2: you can push harder and spend more time, even do
Speaker 2: spikes to evaluate how couple this is really going to be.
Speaker 2: What are the consequences of separating it? Like, it's worth
Speaker 2: spending time on that, just you don't have to do
Speaker 2: it for everything.
Speaker 4: Yeah, and volatility, that dimension of changes or dimension of
Speaker 4: time helps a lot here because once you spot the
Speaker 4: high volatility. By the way, how do you evaluate volatility
Speaker 4: and different ways different models. I prefer the management design subdomains.
Speaker 4: And once you spot a high volatility, then yeah, you
Speaker 4: have a low hanging through there things that first.
Speaker 2: Yeah, Well, and it also speaks to when we're going
Speaker 2: to sign a new piece of this app and we
Speaker 2: see high volatility. Now I certainly have a new opinion
Speaker 2: about where should it reside based on where what the
Speaker 2: coupling looks like. It's like, you know, at least initially,
Speaker 2: I think we should start this in this team where
Speaker 2: it's close to all the pieces it's going to touch.
Speaker 2: You know, if that changes, we can revise that but
Speaker 2: for the moment, especially when say the design is immature,
Speaker 2: it's a new area, Minimizing distance takes a lot of
Speaker 2: risk off the table.
Speaker 1: Yeah.
Speaker 4: Well, let's be honest here, it's not always possible. Sometimes
Speaker 4: when you minimize the distance, you are introducing accidental complexity, Right,
Speaker 4: So I always strive for at least logical boundaries within
Speaker 4: that monolith. Sure, and then we have that distance. It's
Speaker 4: going to be significantly lower compared to the initial distribute monelith,
Speaker 4: of course, but still we can talk about different distances
Speaker 4: within that code base, within that monelith. If we're looking
Speaker 4: at the same module, the same logical boundary, the distance
Speaker 4: is lower compared to different logical boundaries within it.
Speaker 2: Yeah, I appreciate that. That's good.
Speaker 1: Yeah, So is there anything else that you want to
Speaker 1: cover before we say goodbye? Any shout outs or anything
Speaker 1: shut outs?
Speaker 4: Not surely. I just want to remind again that there
Speaker 4: is more to it. There are degrees for the three
Speaker 4: levels of integration strength.
Speaker 1: Right, that's the one you said would take another hour
Speaker 1: to explain.
Speaker 4: Yeah, they provide more detail and they also kind of
Speaker 4: help you to identify those specific cases, whether it's functional
Speaker 4: coupling or model coupling.
Speaker 1: All right, well, Vlad Honenoff. His new book is Balancing
Speaker 1: Coupling and Software Design. Go check it out. It's a
Speaker 1: fascinating subject and we've already had our eyes open. And
Speaker 1: thank you Vlad for talking to us today. Thank you
Speaker 1: so much for having me bet all right, we'll talk
Speaker 1: to you, dear listener, next time on dot net rocks.
Speaker 1: Dot NetRocks is brought to you by Franklin's Net and
Speaker 1: produced by Pop Studios, a full service audio, video and
Speaker 1: post production facility located physically in New London, Connecticut, and
Speaker 1: of course in the cloud online at pwop dot com.
Speaker 3: Visit our website at d O T N E t
Speaker 3: R O c k S dot com for RSS feeds, downloads,
Speaker 3: mobile apps, comments, and access to the full archives going
Speaker 3: back to show number one, recorded in September.
Speaker 1: Two thousand and two. And make sure you check out
Speaker 1: our sponsors. They keep us in business. Now, go write
Speaker 1: some code. See you next time.
Speaker 3: You got your middle.
Speaker 1: Vands do the same. This is hard than my Texas