#177 - Simple Object-Oriented Design: Principles for Writing Clean & Maintainable Software - Mauricio Aniche - podcast episode cover

#177 - Simple Object-Oriented Design: Principles for Writing Clean & Maintainable Software - Mauricio Aniche

Jun 03, 202453 minEp. 177
--:--
--:--
Download Metacast podcast app
Listen to this episode in Metacast mobile app
Don't just listen to podcasts. Learn from them with transcripts, summaries, and chapters for every episode. Skim, search, and bookmark insights. Learn more

Episode description

“Every software gets more complex over time. What we need to do as engineers is to find ways so that we can work with increasing complexity, but not increasing the cost of maintaining the software."

Mauricio Aniche returns to the podcast for the second time and discuss with me his latest book, “Simple Object-Oriented Design”. Our discussion explores the intricacies of software design and shares practical strategies to manage software complexity through effective object-oriented design.

Mauricio delves into the six key principles of a simple object-oriented design: making code small, keeping objects consistent, managing dependencies, designing good abstractions, handling external dependencies, and achieving modularisation.

This episode is a must-listen for anyone seeking to deepen their understanding of object-oriented design and maintaining simplicity in their codebase!  

Listen out for:

  • Simple Object-Oriented Design Book - [00:03:19]
  • No Perfect Code Design - [00:04:51]
  • Managing Complexity - [00:06:37]
  • Object-Oriented Design - [00:08:24]
  • Design as an Everyday Activity - [00:09:43]
  • Effective Iterative Design - [00:12:31]
  • Refactoring - [00:14:31]
  • 6 Principles of a Simple Object-Oriented Design - [00:16:40]
  • Principle #1: Making Code Small - [00:21:06]
  • Arguments Against Smaller Units - [00:23:18]
  • Principle #2: Keeping Objects Consistent - [00:26:25]
  • Don’t Fight Your Framework - [00:30:03]
  • Principle #3: Managing Dependencies - [00:32:05]
  • Separate High-Level (What) and Low-Level Code (How) - [00:33:34]
  • Principle #4: Designing Good Abstractions - [00:36:31]
  • Finding the Balance in Abstraction - [00:38:39]
  • Principle #5: Handling External Dependencies and Infrastructure - [00:41:05]
  • Principle #6: Achieving Modularisation - [00:45:12]
  • Owing to Junior Developers - [00:49:18]
  • 3 Tech Lead Wisdom - [00:50:32]

_____

Mauricio Aniche’s Bio
Dr. Maurício Aniche’s life mission is to help software engineers to become better and more productive. Maurício is a Tech Lead at Adyen, where he heads the Tech Academy team and leads different engineering enablement initiatives. He is the author of the “Effective Software Testing: A Developer’s Guide” and “Simple Object-Oriented Design” published by Manning.

Maurício previously held a position as an assistant professor of software engineering at Delft University of Technology in the Netherlands, where his teaching efforts in software testing gave him the Computer Science Teacher of the Year 2021 award and the TU Delft Education Fellowship, a prestigious fellowship given to innovative lecturers.

Follow Mauricio:

_____

Our Sponsors

Enjoy an exceptional developer experience with JetBrains. Whatever programming language and technology you use, JetBrains IDEs provide the tools you need to go beyond simple code editing and excel as a developer.
Check out FREE coding software options and special offers on jetbrains.com/store/#discounts.
Make it happen. With code.


Manning Publications is a premier publisher of technical books on computer and software development topics for both experienced developers and new learners alike. Manning prides itself on being independently owned and operated, and for paving the way for innovative initiatives, such as early access book content and protection-free PDF formats that are now industry standard.
Get a 45% discount for Tech Lead Journal listeners by using the code techlead45 for all products in all formats.


Like this episode?

Show notes & transcript: techleadjournal.dev/episodes/177. Follow @techleadjournal on LinkedIn, Twitter, and Instagram. Buy me a coffee or become a patron.

Transcript

I think it's expected that every software gets more complex over time. And I think what we need to do as engineers is to find ways so that we can keep increasing complexity but not increasing the cost of maintaining this software. Sometimes people try to say that you should, you know, find a way so that software never grows in complexity. I don't believe that that is really possible. Do you think it's indeed the

other way around? You have to just live with the fact that it will get more complex. Hey everyone, my name is Henry Surya Virawan, and you're listening to the Technically Journal Podcast, the show where I'll be bringing you the greatest technical leaders, practitioners, and thought leaders in the industry to discuss about their journey, ideas, and practices that we all can learn and apply to build a highly performing technical team and to make an impact in your personal work.

So let's dive into our journal. Hello, everyone, welcome back to another new episode of the Technically Journal Podcast. Today I have a repeat guest, so Mauricio Anish, he was in episode 139, so almost one year ago.

Today he's back with his new book, Simple Object Oriented Design. So today we'll talk about software complexity, object oriented programming, and what kind of design that Mauricio is advocating so that we can maintain the complexity of all software and make sure that the design performs well and we can always maintain the software. So welcome back Mauricio to our show. So looking forward for this conversation. Thanks, Harry, It's a pleasure to be back.

Right, Mauricio, So it's been almost a year since we last chat with each other. So maybe if you can give us a glimpse what you were up to in the past one year? For sure. So for those that didn't listen to the previous one, my name is Mauricio. I work in the software industry. For 20 years I've been a developer, I've been a full time researcher.

Now I'm back to industry and in the past 2 1/2 years I work for Ajin. Ajin is a financial technology company and right now my current duties are to lead the team that we call testing enablement team. We do basically tools that help developers to create tests. We do code quality related tools and this type of stuff. So yeah, that's this is what I'm up to. Hey, thank you for being part of the technically journal community. This show wouldn't be the same without your ears and you are

the reason this show exists. If you're loving TLJ and want to see it keep on growing, consider becoming a patron at Techledjournal dot dev Patron or buying me a coffee at Techledjournal dot dev coffee. Every little bit helps field the research, editing and sleepless nights that go into making this show the best it can be. Thanks for being the best listeners any podcast could ask for. And now let's get back to our episode.

Yeah. So I think for those of you who haven't listened to the previous episode by Mauritio, right, we talk a lot about testing, effective testing in fact. So there are a lot of insights. I learned a lot from that episode. So make sure to check it out as well if you haven't listened to that episode. So today we'll be talking about your new new book, Simple Object Oriented Design. So maybe in the beginning explain to us like why you came up with this book.

It seems like a little bit different from the first book that you wrote a few years ago. For sure. And actually, I just got a copy here. So if you're watching the video, you can see the beautiful cover that Manny has prepared for this one. And I think the motivation for this book was that one of my best friends, Alberto, him and I, we always talk about code design, right? How to design classes so that you can implement your business focus system in in a nice way.

And Alberto loves design and we talk a lot about it. And all those conversations, they triggered me and I felt maybe I should just, you know, try to write down the best practices I observed throughout my career and hopefully in a very short and concise way. So this book is quite short. I think 150 pages is the final printed version. So that's how this book came to be.

And I think one of the premises that I had there was that I went in a book that doesn't strive for the perfect design, right? Because it feels like to me, sometimes I read a book about objectory and the design, and the design is perfect. And that assumes that the person knows a lot about the problem already. And that's not what happens in real life, right? You get a very messy requirement that you don't know so much about it that is going to change. And you cannot go for the best

design ever. So you have to take pragmatic decision so that you can move forward and deliver softer. So that's what I wanted to try to summarize in this book. So you mentioned that, I mean your career 20 years, right? So you opened the book by saying that after 20 years being in the industry previously, you try to achieve perfection, you know, maybe perfect code design, but you currently don't believe that

it is true anymore, right? So you think that probably there's no perfect software out there. So tell us your revelation about this, and maybe what we can learn from that revelation as well. Yeah, for sure. I think for me, it was when I learned Objectory entered design more than 10 years ago, I thought this was just beautiful. You know, they could create objects and they could talk to each other and then if you really do your best, then those objects, they just look so

pretty and so elegant. But in real life, life is way more messy than the examples you see in books and things change in real life and etcetera. So I feel like it's, and I think in my 20 years of experience, although right now I don't like business logic, right? So I mean platform engineering. So I'm more writing tools for developers. But in my previous experiences, I was always, you know, a back end developer implementing business rules.

I guess like a lot of the people out there, it was never possible to achieve perfection, right? We always had to, you know, at some point settle for something and and we should be OK with that because that's just life, right? So we don't want to take the most awful decision possible, right? Because it's really bad. That creates technical debt. But you also don't have to strive for perfection. Something in between is good

enough. Yeah. So I think over in my career as well, especially when we work with multiple people in the teams, right, with various degree of experience. So I think it's really hard to maintain quality across everyone, right. So sometimes we have to let go some parts of it, but as long as the maybe general high level design is good, we can always maintain and maybe refactor from time to time, right? Which brings me to the next discussion about software

complexity. So I think it's very funny. I mean, we all have been there. We try to build a new system right from scratch. We think that this is going to be different this time, right? So we'll build a perfect software, but I think over the time software gets complex and again we come to the same cycle that software is becoming quite complicated to change. So why software can become complex over time and what we should do in order to maintain that? Yeah, for sure.

So software becomes complex just because lives gets more complex, right? So we're sort of trying to implement software to solve people's problems, but those problems get more complex. And if you don't pay attention, your code will also get complex. And that's natural. I think it's expected that every software gets more complex over time. And I think what we need to do as engineers is to find ways so that we can keep increasing complexity but not increasing the cost of maintaining this

software. Sometimes people try to say that you should, you know, find a way so that software never grows in complexity. I don't believe that that is really possible. Do you think it's indeed the other way around? You have to just live with the fact that it will get more complex. Yeah, especially as you keep on building on top of what

existing, right. So I think more business logic, maybe new features, right, sometimes edge cases that you didn't think before and somehow it gets introduced, but it just doesn't fit rightly, right. So in order to make it quick, so you just add as quickly as possible as well. So I think I do agree especially for business related software, right? I think it tends to get complicated especially these days. People also over complicate the architecture, right.

So maybe using micro services or maybe different databases and things like that. I think it makes the software get more complicated as well. So I think another thing that I am interested in your book you cover object oriented design. I think these days many people kind of like have different flavour of programming languages. Object oriented design and functional programming probably are the two biggest. So maybe your view is object

oriented still relevant? Or should people think about some kind of different paradigms? I think all those paradigms are good enough and you can build good software with all of them, right? The functional programming, data oriented programming that is getting more traction right now, I feel it's just about picking the the paradigm that works best for you. And for me, object oriented programming was always the one that my mind could understand better. So I don't think it's a bad decision.

I think if we start a project today using object oriented programming, it is a good choice. You will be able to write maintainable software for sure. Yeah. So I think you mentioned as well in your book that object oriented helps you to build maintainable software, of course, if done right. But I think, yeah, it's also a very nice abstraction in a way, right? So you can kind of like thinking objects, right? So some people find it more

intuitive. I do find it more intuitive as well, but I think different kind of problems. Also sometimes you can choose different programming paradigms which may be more suitable. For example, maybe data pipeline kind of thing is more suitable for functional programming. So yeah, let's probably cover the object oriented part this time. You mentioned that we always have to keep design as part of our activity in software

development. So I think it this is probably intuitive in a sense, but not practiced a lot by different people because they assume that the design is there, I just need to add on top of what exists, right? So tell us, how can we do more design activities in our software development? For me, one of the greatest powers in object oriented programming is that you can create abstractions and then abstractions.

They reduce the complexity because suddenly you don't have to handle specific implementation details, you just focus on the abstraction. Those abstractions are more extensible. They provide usually extension points, so one and so forth. So object oriented programming can be very powerful if you're always thinking of how to make sure I'm building the right abstractions.

And when I say you have to always be developing with this designing minds, it is because if you don't think of abstraction, then you know, how should my classes look like, what should be my extension point, so on and so forth. You know, you're just going to go to the code, then you're going to just write the implementation that solves the problem. Then let's say maybe this is, you know, 10 ifs in a row that solves the problem for sure.

But this is maintainable. And when you have the design in mind, you're like, oh, OK, it feels like those I have to implement those ten different business rules and 10 if statements are the easiest possible way for me to do this. But this is good design. Is this going to help me in maintaining this in the future, in testing this and evolving this? And if you make this into a day-to-day activity, it hurts you less.

And you mentioned when you're talking about my previous comment that you know you're designing something and then you have corner cases and etcetera. And those things really hurt your abstractions, right? That's the real life I want to bring into this book, right? That you create this beautiful abstraction. It seems like it works. You deploy and then suddenly you find a corner case that your abstraction doesn't fit. And that's just normal.

But then once you've come back, you have to know, has your design mind set in mind and do I need to improve my design? Or if I just add this if statement here, is this like a huge sin that I'm making right now? So as long as you have you're always thinking of the design, I think you're fine. If you're not thinking of designing, just coding, then I think then you're not really using the power of object oriented programming. So I think that's really, really insightful, right?

So think about abstractions and always try to look back whether the existing abstraction or design actually fits the problem as it evolves. And I think you also quoted multiple times in the book, right, John Osterhalt And one of the quote is actually talking about effective design actually comes up after multiple iterations, right? After maybe one or two or three times you work on the same problem, right? And somehow you find the right

design afterwards. So I think what I can see in practice in the real world is that sometimes we come up with first design, have few problems along the way, but we didn't actually refactor or redesign and we just built on top of it until it gets so complicated that we probably think of, you know, we should rewrite this. So tell us, how can we actually have more dare to actually do a lot of redesigns throughout all the software development? Indeed. So that book influenced me a lot.

It's a book that I read much later in my career because it's more of a recent book, right? But it has so many good gems that I really loved. And although I don't agree 100% with that book, right, I think I even write this in my book that there are some parts that I don't believe, but he does mention that in his experience, he only gets to be perfect between coats design after the third time he's working on that

problem, right? So you try once, then you learn something, you try the second time, you learn more, the third one looks a little bit better. And when I read that paragraphs in the book, I was like, yes, that makes a lot of sense. I feel like for all the things that I did something really beautiful and elegant and flexible, it was because I already knew the problem for so long.

Then I had tried already my first POC and you know, there was a realization for me that you will never do something in your first shop that really looks elegant in the way you want. That then leads me to the argument of my book that is if you know that's just what's going to happen, you are not going to come up with something

elegant from the first try. You have to keep monitoring yourself and as soon as you learn a little bit more than you have to invest in refactoring these and improving it. Because if you do these at the very beginning, odds are then it's cheaper than doing this five years later where the code base is much bigger, everything is much bigger.

So I feel like putting refactoring on the loop and not being ashamed of, you know, let me change a design decision that I took because like 3 months ago, but it is already a wrong decision. Maybe it's cheaper than if you do it later. So that that's sort of the the argument I I make there. Right. So I think refactoring is always some things that it takes

discipline, right? So from every developer to actually have the, you know, optimism to continuously doing it, also having effective testing. So it's very important because when you refactor, you don't want to break previous features or have some regression box. And I think importantly as well, like use different kind of tools to help you in the refactoring. Maybe use the modern ID ES, right? Or maybe use AI these days. They can help you explain the code and maybe do kind of like a

local optimization. So all these things should help you to support your refactoring activity and hopefully the good design can come up from there. Yes, I was going to say tooling is important, knowledge is important, but also culture, because I've been to teams, every factoring was just part of the culture and people would really appreciate when they're doing this type of commits in the codebase, right.

But I've also been to teams where this was so as you know, as a bad thing, you know, like you're not being productive, you're not really delivering business rules, new functionality and etcetera. So I think culture is also a big part of how we make it possible. If you're in a culture where refactoring's well seen, then lucky you just do it right.

If you're not, then maybe you should first change the culture rather than trying to introduce new tools or knowledge sharing sessions because they will not be enough. Yeah, I think that's a very good pointer, right? So culture is very important indeed. So sometimes developers may be afraid to say, I am refactoring this design because it wasn't right. So people may think, OK, why didn't you do it right in the

first time? So I think a good culture, especially the people around you who support refactoring as well, I think that makes a big difference. And probably I would as well. A coach that probably can help you point out like which parts that should be refactored. I think that also helps as well. And also why doing this at the beginning makes more sense, right? Because I can understand if a developer blocks a merger request that does a refactoring five years later where the code

is way more sensitive, etcetera. It is much harder to block such merger requests if it's at the beginning when the code is still fresh. You know, just out of the oven if you will. So that's why it needs to be constant. Yeah. So let's move on to your six principles of how we can make a simple object oriented design, right. So I think the keyword here is simple. You mentioned it a couple of times as well, simple. Sometimes it's not intuitive as well.

That is simple, right? So maybe if you can walk us through the six characteristics or six principles that you outline in the book at the high level, and afterwards maybe we can go slightly deeper, 11 each in the next conversation. Yeah. For sure the first one in the book is making code small and the intuition there is that code that is small. So think of lines of code. Even a code that has less lines of code is much easier to maintain that a method that is super large.

Right? So one of the arguments I made there is that you have to make sure that your units are always small. It's better to have multiple small units than one super large unit. Then I also talk about keeping objects consistent. So if you're in an object oriented system, your objects hold data and those objects commonly relate to other objects and they together represent some sort of very complex business

functionality. And it's very common that you change something inside of the object and you have to propagate this change and make everything consistent, right? So if you have, I don't know, a shopping cart and a list of products and items, if you add a new item, you need to make sure that the cards has the new total price to pay, for example. This is what I mean by consistency. And if you don't pay attention, it's very easy for you to end up in a design where things are inconsistent.

So maybe the list of items in your cards, if you sum all the prices of them, the sum is different from the total value you have in your shopping cart object. And this is of course very problematic, leads to bugs. So you got to make sure that they are consistent at all times. Then I talk about managing dependencies. So in object oriented systems you depend on things from, for example, one class depend on another class, or a module depends on another module.

And then there I discuss a few rules on how we can manage those dependencies. Because you cannot avoid dependencies, you have to depend on smaller classes to make the whole behaviour work. But how do you make sure that those dependencies don't hurt you so much? I didn't talk about abstractions and how to design grid abstractions and to meet this chapter is key because this is key in object oriented programming.

Making sure that in in parts of the system that we need a lot of flexibility, making sure that you have a good abstraction there so it's easy to keep adding more business rules or making sure that you can easily change something that happens in a very complex slope is very important, right? So the example is you don't want to, you know, if you're doing this very complex system that has to calculate taxes for 100 different countries, you don't want to have 100 if statements

in one single B class. And you want it to be easy to add country #101 because apparently that's part of your business, right? So this type of stuff is very important. If you're looking for simple objectory and design, then I talk a lot about how to handle external dependencies and infrastructure. And what I mean by that is when you're designing your object oriented system, you're thinking of classes and how they relate to each other and how they keep consistency among each other and

blah, blah, blah. And this is all beautiful and isolated, right? This lives in this design world, if you will. But at some point, your system will have to talk to some external system, let's say a database, right? You have to persist stuff into the database, or you have to make a web service call to an external party, or you have to use a library that you didn't write yourself. So it is very common that in the systems you have to integrate with things you don't really own

and control. And if you don't pay attention, it's very easy to, you know, get super coupled to this external infrastructure or suddenly your code is hard to test because you depend on this third party, so on and so forth. And finally, I talk about modularization. That is, in very large software systems, you sort of have multiple domains within one design. And then of course, you don't want to have this in one big

messy bucket. You want to have different modules and those modules talk to each other. So this is almost like seeing, you know, the coupling between classes that I spoke at the beginning of the book, but now we're talking about modules, modules talking to other modules. This chapter makes a lot of sense if you're really working on a very large software system. So those are the six principles that I cover in the book.

Yeah. So I think indeed those are the six principles, 6 characteristics that you think will lead us to a simple and better object oriented design, right. So I think let's probably try to cover each of them in a more details. So the first one is something about making code small. So you mentioned about, you know, maybe lines of code, maybe

smaller classes of functions. So is that the only thing that we should care about in terms of making code small or are there any other practices that we should think about in terms of trying to make our code small? Yeah. So for me, rule of thumb number one there and the most important one is try to keep your units very small. And by units I mean your methods, try to keep them small and your classes, try to keep your classes small.

And if they start to grow, and they will start to grow at some point, refractory #1 is how can I move some code around so that it leaves this big class and then it goes to another class or to another method, right? Maybe your public method starts to grow and then you do that distracting method of refactoring that everyone sort of knows your ID does for you, you move to a different method. So that's sort of the gist of what I discussed in that chapter.

And my point there being that if you're in a small units, you are going to understand it much, much, much faster. And when you move a piece of code to another class, you're basically saying, I, I don't have to read this piece of code when I'm reading this unit that I'm focusing on right now. And if you give, let's say you extract a bunch of likes to a new class, you're going to give this Class A name. You're going to give the method a beautiful name.

So when I'm reading the original class and I see a method call to there, that method call explains to me what is going on. I don't have to read the implementation details of that. So I feel like once you're forced to only work in small units, you're forced to write code that explains intents first and then implementation later. And finding ways not to read implementation is what makes you more productive. You don't want to read every detail, right?

Once you're, you know, maintaining something, you just care first of all the intents and then you go crazy and debug whatever is going on. So that's sort of my arguments number one in the book that I think it's not so hard to follow and I think it does make a lot of difference. Yeah. So I think intuitively it's not so hard to follow, but I think, again, in practice, it seems very hard to have the discipline to actually continue doing this,

right? So one of the arguments, probably some people might have these arguments. So jumping into multiple methods or classes breaks the flow. You have to open multiple tabs and, you know, navigate through different files. And the second thing is about finding good names. Yeah, of course we have to be able to explain the intent, but sometimes finding a name or function name or method name or class name is not so easy. So maybe if you have tips for these two things.

For sure. And your first argument is a common argument that usually people that defend having everything in one place usually raise right. That is, I don't want to navigate through different classes and methods and etcetera. And the point is, if you're really designing with abstraction in mind, you won't have to jump into those other classes as much as you think you would.

Because usually you know, what you're doing is you read the intent and then you find out this is the specific part they need to really dive into. And then you just dive into that specific part. If your design is forcing you to read 1000 lines of code every time you have to do some maintenance in that piece of code, the abstraction is wrong. So I think that's usually my response to that. Navigating isn't so much of a problem today anymore, right?

IDs are also very good. So even if sometimes you have to do some navigation, just learn how to use your ID and that's will make you way more productive for sure. And then the other comment from you was the naming. Yes, indeed, naming is very tricky. I don't even attempt to give a lot of tips on how to name because I should like the the best one is keep trying, keep refactoring. As soon as you learn more about

the domain. I think that's actually the rule, like the suggestion I give and they will, you know, just keep trying. Just keep refactoring. The more you learn about the domain, the more you you rename it. But once you get to a reasonable good name, it doesn't have to be a perfect name, but a reasonably good name, then life's so much better, right? Because again, you just read the method call and they don't know what it does without going to that method.

And also maybe maybe a comment just said it's, it's very hard. It is because you know, life is hard. And then sometimes you're like, where do I break this code, right? Maybe you don't find natural spots for you to extract something to another method or to another class In my book, I'll give an example, right?

Sometimes you try to extract something to a private method, but then you have to take together, you know, you create a function with 10 parameters, you know, like, yeah, do I want to function with 10 parameters? You know, that it's very real to me and blah, blah, blah. So that is the hard part in real life is because sometimes there's no natural cutting point for you to extract something, but you have to, you know, keep working until it emerges and you find them.

Yeah, So I think thanks for the tips, right? So for those of you who still find it difficult to extract the units to become smaller and smaller, please do give it a try. I see some developers also try to create sections within the big function by putting in comments. So it's like giving names to a certain sections, right? But it's still one big line. So instead of doing that, maybe extract as a private method and give it a proper name that expressed the intent.

So I think those are really, really great tips, right? So please try to make your units smaller. So let's move on to the next principle which I find this is also tricky sometimes. In object oriented, you mentioned about keeping objects consistent. I think in some theory they call it invariance as well, right? So when you create objects, right, it should maintain the invariance within that objects. So tell us more about this inconsistency, right? Why it is a problem?

Why? It tends to create a lot of bugs. Yes, sure. So by consistency, I mean whenever you have an object, you have to trust on the state of that object, right? So if you have a shopping cart in your hands, you should call the method. Get me the total amount of these cards. Give me how many products are there? You have to trust the answer. The answer should be correct. You shouldn't have to do a lot of work yourself to make sure that the object is in the correct state. It needs to be.

The object needs to do it by itself. But it's easier said than done, of course, because as soon as objects start to grow and then they start to depend on other things, and then suddenly you have this very complex aggregation of objects representing some very complex business rule or whatever. Then it gets really tricky because you know, maybe one object super down the line changes something and this change needs to be propagated upstream and downstream, whatever.

And I think that's what you need to make sure this is when the program starts to happen, right? In this very complex entities, at least in my experience and in the book, I tried to make a discussion because to me, the discussion on this is where to ensure the consistency. Who should ensure the consistency? Should it be? So for example, the first argument that I make, it should

never be the client, right? So the client of the class should never be the one sharing consistency should be somewhere else. But then in the book, I go into the details because sometimes, you know, to ensure consistency, you also need to make a database call because maybe you need some data from the database. And then usually we don't like to make database calls from entities, right? Because you don't like to couple entities with databases.

So then this means I cannot really do the consistency check inside of my entity. So this starts to accumulate. And then in in the book I give some rules of thumb. For example, from the 1st place you should try is the entity itself. Maybe inside of the class. If all the information is there, just do it there so you don't need to go somewhere else. But then I need a database call or whatever. Then maybe I have to introduce something in between that ensures that that object is

always in a consistent state. This is less ideal, of course, because then, you know, you have to make sure that clients don't, you know, they make the call to the entity without going through this small day you had to create. So you have to play a little bit with your design to make sure that this happens or this doesn't happen, that that

probably doesn't happen. But this is a trick and maybe I went to technical right away, but then if I zoom back a little bit, I think the the point is you got to ensure your objects are consistent and you have to find a place to make it consistent and you have to make it simple for the clients. It should not be the responsibility of the clients to ensure that the object is consistent. I think that's indeed a very important point, right?

You have to ensure that the client does not understand how to assemble your consistent object, right? So to speak, right? So I think sometimes this tends to happen if let's say you are a single person trying to work on those multiple classes, right? Because maybe the understanding leaks all over the places. So you unconsciously create that behaviour simply because you were not so conscious about the

design, right? And I think sometimes also frameworks kind of like lead us to that thing, right? So for example, in some frameworks you have the getter setters that are exposed as if like you can call that publicly. So I think I can see some legacy code last time right here, you create a new object, right? And then you have set one, set two, set three, set four. I think that's also like a best practice. And I think you mentioned a few times about entity and maybe aggregates, right?

So I think using domain driven design as the concept to maintain a good design is something that is very, very useful for us to implement. So if let's say those people who are stuck with their framework, what can you suggest in order for them to be conscious about it? That's the first thing. And also try to adopt A better design. Yeah, good question. Because frameworks are opinionated, right? And they do this because they want to give you more

productivity. If you just follow their rules, things are much better for you. And the suggestion I make in my book is don't fight your framework. As soon as you pick your framework, accept its decisions and see what you can do so that they don't really harm you that much. I feel like modern frameworks, all the days, they are quite less opinionated as they were in the past. So that makes your life a little bit easier. You have more freedom in design.

But some frameworks from the past, indeed they were quite opinionated. I remember suffering a lot with JSS, you know, and this type of technologies from the days. So don't write your framework. And why do I say this explicitly in the book? Because I see sometimes advice on how you can, you know, abstract your whole domain model away from your framework or for whatever other decisions you take. So, you know, it really lives in

a bubble. I feel like great, you know, maybe for some very specific cases, you really want to abstract everything away from your domain. But in most software systems that I've seen, it is OK to be coupled to the framework. You're not going to move from frameworks every month or so. So I think it's also about finding the right balance between I want to be productive and I want to have a perfect design, you know?

Yeah, so indeed, right. Sometimes framework gives you a boost of productivity, but also do understand the best practice, right, in terms of design and what we should avoid in terms of how to make, for example, the classes, function calls and things like that. So be aware of that. And I think one thing very important to make sure object is consistent about validation, right?

So having the precondition or maybe some kind of validation roles before you create a object or execute something is very, very important. Maybe let's move on to the next one, which is about managing dependencies. I think the very, very first advice you give is minimize dependencies. The less the better. So how can we minimize dependencies? Yeah. So why do I think this is a good advice? Because if you have a class that depends on other twenty classes, everything becomes more complex,

right? The the code is just more complex because it probably makes calls to those 20 dependencies. Those dependencies do stuff, they change state in the system, and then you have to handle all these things. So this just increases complexity. And of course the natural problem of coupling that is if one dependency changes and it depend on that, maybe if it's a breaking change, that will also impact you. So I think the less dependencies

the better as a general rule. But you of course, you have to always depend on stuff. And then the whole point of the exercise is how to find this balance. And for example, it's maybe instead of depending on 10 classes, maybe you can group some of those dependencies. So you get three of those dependencies and then you group them into another class. You give a beautiful domain related name to this class that groups these three other

dependencies. And the original class now only depends on one set of depending on the other three. So very more often than not, when you look at those classes, you can find groups, things that you can group and give a new domain term for this operation and move this to another place. So this is usually refactoring number one that I feel quite feasible for a lot of the business systems out there. Yeah. So I think that's a very, very useful tips, right? So if you start seeing a lot of

maybe imports, right? So in a lot of programming language, you can sense dependencies by looking at the import statements, right? If you do see a lot of classes that you imported or dependencies you import, so maybe that's the first sign, right, that you maybe should try breaking them up into multiple units, right? And I think another useful tips that I find in your book is you mentioned separate high level and low level code, right? So separating the what and the how.

So tell us about this principle and how we can implement this. Sure. So I mentioned before in this interview, right, that I think to me, nice code is the one that you, when you really read the intent and not implementation detail. And this becomes very important was you're in a very complex business rule that is not only complex from an intent point of view, but also from an

implementation point of view. And there what you want is you want to open the first class that implements the business rule and you want to read like a 10/15/20 lines that look like an algorithm. It only expresses what happens, not how it happens. And this is to me, the separation between high level and and low level, right? And you don't want this for your whole system, right? Because you don't need this for the whole system.

But for the complex parts, what you want to do is to have this entry point that is purely intense and then you let low level classes implement the intent and do the job and do the hard work, right? So this is the separation that I discuss a lot in my book. And I think it's very important because it does help you to manage dependencies, right? When your dependencies focus on intent. So it's more high level. Think of an interface, right?

Imagine you have some business rule and you have 10 implementation of this business rule. So you give it a common interface. This interface expresses intent. It's it's high level and it's very stable. So if you're depending on such an interface, you have a dependency there. But this dependency doesn't hurt you so much because interfaces like these tend to be very stable.

So what you have is for this part of the code base, you want to have code that expresses intent and only depend on high level things like interfaces and abstract classes. And this tends to be quite stable. And then lots of flow level classes that implement those interfaces and get the job done. So this is funny because this section of this book, I was always in doubt, should it fit this into managing dependencies or design good abstractions?

And in the original, one of the first drafts of the book, this was one chapter, but it became too big that I decided to break. But those two chapters, this one and the one we're going to discuss next, they have a huge intersection. So I think that's a very useful tips. Again, from my understanding, maybe if I summarize it, so you separate the high level intent from the low level

implementation, right? So if the client always refers to the high level intent, they don't actually need to see all this low level dependencies. And hence you actually manage dependencies by minimizing those classes that they need to import or they need to understand, right? So I think that's a very useful tip for a better design. The next one you mentioned about good abstractions, right? I think this is sometimes tricky.

So finding good abstractions is always difficult unless you kind of like have a well defined business rules that stays the same. But if it's like a novel software, I think it's really, really difficult to find good abstractions. How can we find a better abstractions, Mauricio? Yeah, that's the $1 million question. I think finding the perfect abstraction as I've been, you know, restating here is impossible. What you want to find is something in between that solves the problem.

And I think that that comes to me, it always comes from trying to understand what will evolve, what I believe it's going to evolve in my system. And maybe a concrete example, many years ago I worked for this company and I was part of the billing team. So we will do everything related to how we charge the customers up to later calculating the taxes we had to pay and etcetera. And it was so complex, right, because the company was offering

a lot of services. There were lots of different ways to charge the customer, but also to pay for taxes later on. So we we sort of understood that, you know, charging in different ways was something that had to be core of this. And also calculating different ways of taxes was also something that recorded business. So we spent a lot of time trying to think of how can we create an obstruction to represent building and an obstruction to represent taxes.

And it was a huge exercise of us talking to the business people, understanding the different types of taxes, refining the abstractions until we got to the point that whenever there was a new way of charging someone, we would just, you know, implement a bunch of interfaces and voila, this would plug naturally into the system. And the same for Texas. So this is, I think that that was sort of the example I had in mind when I wrote this chapter about good abstractions, right?

So if you want an abstraction that's truly represents the intent of the system, because you know the intent of that part of the system, because you know it will evolve. And you do this by thinking of how can I give extension points? How can I facilitate the extension of this design for the future, right? And I want the person to come back and write if statement #20 to implement the business rule. Like I want to simplify this a little bit. Does that make sense to you? Yeah, yeah.

I mean, in a sense it makes sense. But some people take to the extreme, right? They always think in terms of possibilities. You know, they create too many abstractions sometimes, right? So I think that kind of like also complicates the software unnecessarily because sometimes what they predict doesn't really happen. And some people have this principle, Yagni, right? You ain't going to need it. So how to find the balance between good enough abstractions

and maybe too many abstractions? Yeah, but the best thing would be to have a crystal ball, right? So we could predict precisely what will change and what will what change. And we make mistakes for both sides, right? So we sometimes over design and we create something more complex for something that doesn't have to be complex. And other times we under design. So we create something too simplistic. And I think of course experience is something that helps.

As time goes by, you learn better to listen to your requirements, product manager person and then understand if you need an abstraction there or not. The other one is you try to observe. So you start very simple. Don't over complicate some of the beginning unless you have a clear reason for it. And then as you go, then you had an abstraction. But something that I also say in my book that is, I feel like we said so much, don't over design, don't over design, don't over

design that I should. The main reason, the main problem right now is that we are under designing things more than over designing things, right? So a joke that I always make with my friends was I never saw a person saying, you know, calling the wife or the husband or or the significant one and saying, Hey, honey, I'm I'm stuck here at job because, you know, this code is full of beautiful interfaces and etcetera. It's usually the other year

round, right? Hey, honey, I'm late because this code has 3000 lines of code with no single abstraction that I can understand. I have to guess them from the implementation. So I also feel we we are always spending, you know, we go to the extreme. So you got to find the midterm there.

So, and I even say this explicitly in the book, don't be afraid of sometimes doing over design because if you feel like this is going to meet an extension point in the future, go for it. Don't be afraid of that. Yeah, like you mentioned, maybe we do need a crystal ball, right? But I think the essence of the your advice here, don't be afraid, right? Sometimes I think we will make mistakes in terms of our abstractions. The key here is to refactor it,

right? Make changes whenever you understand the problem better and you find a better way of designing it right. And again, having good test cases that covers not just the unit test but also maybe integration or acceptance test can actually help you in order

to make these redesigns. So the next is about handling external dependencies or infrastructure, so things like database calls, web service or maybe other things that you depend on. So why do you think we should manage all these external things better? Yeah, good question. So no systems leaving the vacuum, right? At some point you're going to have to integrate to another system being a database or web

service, so on and so forth. And so they have to find ways to connect your beautiful object oriented design to an external system that speaks a different language, that has a different abstraction than you have. If you don't think so much about it and let's say you just code everything in one big class and etcetera, suddenly you're very dependent on that abstraction on that system. If that system changes, it's going to propagate a lot of changes to you, harder to test,

so on and so forth. So you have to explicitly think about the external systems, external dependencies, and how you handle them. And I give a few tips in the book, and the first one is, I find it quite simple actually to do is separate infrastructure from the main code. Whenever you're going to have to talk to a third party, let's say a database, just don't do this in the same place as the

business rules. Just create another class whose only responsibility is to translate your domain entity to a database call, let's say, move it to somewhere else. Don't let them stay together. That will already improve a lot. If there's a change in your database, you just have to

change one point, right? You just go to your database size and you just change there your entities and your domain is more testable because it's easier for you to replace that small part of the system that is a database with a mock, for example. So only advantages there. I also make a discussion. That is because I feel like sometimes you read books, people on the Internet saying you should hide the external infrastructure as much as

possible from everywhere else. Like your code should not know if you're using a relational database, your code should not know. You know that you're using a no SQL database, whatever. And I feel like this is such a bad advice, right? Because if you really want to write something that is fully agnostic of the many technologies you have to choose to deliver software, you're going to have to write so much code.

And it's way more complex and way more likely to have a bug and probably doesn't use your infrastructure to the best. Because if you create something like a mustacre, sort of, you know, letting down everything else. And I understand where this comes from, right? Because back to my first point here, you don't want to make your code really coupled to the external party, but there's a

limit there. And usually my argument in the book that I make is what you need to do is you, you have to hide those decisions from the code, but not from the developer that is maintaining that code, right? So the developer knows it's, you know, a relational database behind the scenes. So, you know, you can use all the advantages that relational databases give to you. But if you look at the code, the code of course knows it's a relational database with a lot of it is a little bit abstract

the way. So you just make a call, you know, my repuls will not say someday you hit this from the client code a little bit, but you're not really hiding it completely. The developer knows, it's really clear that it's a relational database behind the scenes. So that is my main point there, that you have to separate these things, but you're not pretend you don't know them, you know them. And it's good that you know them, because you can take optimal decisions, right?

Yeah. So I think that's a very, very important point, right? Separate infrastructure from domain code or your business logic, right. So I think again, coming back to domain driven design, this is one of the key principles as well. And I find that design patterns such as hexagonal or ports and adapter kind of a pattern, right, fits nicely into this, right, If you follow that principle along. So you will tend to separate the infrastructure from the business

domain. And I think, yeah, what you mentioned about don't try to hide too many things, right? Because sometimes, especially in my previous experience, right, sometimes even your relational database, right, could use multiple read replicas, right? And hence it becomes eventual consistency. So I think if you try to hide it, it's very, very difficult, right? And hence I think it's a very, it's actually OK to actually reveal this, but maybe not in your business domain logic,

right? So I think try to create an abstraction such that the developers can actually understand what is going on. So the last one is about modularization. So I think maybe once you create an aggregate or bounded context, right, you probably create a modules or sometimes also packages, right, if you want to distribute your SDK or something like that. So I think one very, very useful tips that you write in the book is about deep modules. So tell us what is this deep

module is all about? Yeah. So the chapter of the book was there to uncover the problem that is sometimes your different domains, they grow very much, right? And then maybe back to the example of billing. And then maybe let's say billing is dozens of classes to make billing and then they also have text. And then maybe you also have systems that, you know, notify people that send them invoices via e-mail and whatever. And all those domains, they grow

very fast. Maybe you even want different teams working on them because you're that big right now. You know, how do you make these modules to communicate each other? Because although they are isolated, they still depend on each other. You know, billing might send a message to text and billing might send a message to notifications, whatever. So this is to me the moment where you need modularization.

And I copied the term deep modules from John from the Philosophy of Software Design because I really loved that term. But I think I, I rephrased it a little bit to my own point of view. That is, I feel like something we want to for sure come back is that idea that everything needs to be a module, right? You don't want to have 1,000,000 modules or 1,000,000 micro services because it also does whenever you speak stuff, you win something but you also lose something.

So you want to find the right trade off there. And deep modules is basically the idea that what you want is a module needs to do something very big, so to say in quotes, right? It has to have like a beefy responsibility. Like for example, this is the entire billing domain. This is 1 module that I have, so it is not just one class, you know one class. It should not be a module in most cases.

So it does something really busy, really important for the business and it also offers a very simple layer that simplifies all the complexity that is inside of that module. This is the type of module you want to have in your system, right? Complex stuff that is exposed to the outside in the simplest way possible. So that is the idea, so that the clients, others that want to communicate with their module, they have to do the least amount of effort to be able to send you a message.

And I think this is where, to be honest, where the challenge starts because it's creating a simple interface on top of something very complex. It's tricky. I can't remember now that it's in my book or in John's book or maybe in both that there are ideas, you know, like come up with sensible defaults, right? You don't have to force your client to have to configure every single possible variable that you have in there. So for example, this is one

example. Simplify things and this is where you need design again, right? Yeah. So I think deep modules here, I really like it, right? Because sometimes when we create modules, we don't really think about the interfaces of the API or the contracts, right. So I think the key essence here is like simple interface. So you abstract away all the inner details by exposing those simple interface. And yeah, in in fact the complexities is hidden inside

the module. And I think a few other things that I also like in the modularization part is actually think of like the module, even though you build it within your own team, right? You should think as if like you're going to publish it to another team or another company, which is beyond your control. And you cannot kind of like give them a close guidance. And the last thing, of course, like don't leak out the inner details, maybe like your database tables, columns and things like that.

So I think that's definitely a bad practice. So Mauricio, thank you for giving us the walkthrough of the six principles. Again, there are so many things in your book. So for people who love this conversation so far, I'm sure you'll get a lot more insights by reading the book again, the book is not a thick book, right? So probably it's like 150 pages. Like what Mauricio said. You can actually finish it in a few days.

So please do check it out. I think I find this book, even though sounds simplistic, but looking at my career experience, right, this is not something that is commonly practiced. So I think in the last part of your book, you also mentioned about being pragmatic. So one particular thing that I love about being pragmatic is you talk about for us to do this simple object oriented design because we owe this to our junior developers. I find this quite beautiful

message. So maybe if you can enlighten us a little bit about this. For sure. So whenever you're coding and you're trying to be pragmatic, remember that at some point there will be a junior developer that will maintain this. So it is our duty to make sure they can learn from the code we're writing, right? When I was a junior developer, I was learning a lot from what I was reading. So you own this, the junior developer, you're directly, you're indirectly training.

So that was sort of the gist there. I'm really happy like this. Yeah. So I think the onus is on us, right? So every time we leave the code behind because the code tends to leave for quite some time, right? You don't always rewrite everything every time. So I think the next developer who comes in and try to understand your code will refer to your code design and most likely they will just build on top.

So if you have a messy code, obviously they will learn a messy thing and maybe they'll bring it forward to another projects or software that they are working on. So I think the cycle continues and hence we probably have a bigger problem in the software industry. So thanks for this conversation. Mauricio, I have one last question which I asked you last time in our conversation before, right? I call this the three tech

leadership wisdom. So I'd like to hear again from you if you have a different wisdom this time. Yeah, good question and I will try to give suggestion based on the discussion today. So I guess it will be a bit different from the previous one. So the first one is focus on your technical debts because at some point it can become impossible to pay. So everything that we discussed of refector early and etcetera, I truly believe it makes a difference in practice. Then focus on quality.

So OK, that's not so different from the first conversation we had, Harry, but I guess I like quality. Don't let it be a second class citizen in the process, right? Find ways to make sure quality is just part of the process, part of the culture. And finally, something that is a lot on my mind right now, given the team that I'm working with, is focus on developer experience. Because coding should be fun and fast. It's not boring and slow, but once you grow, it is very hard

to keep things that way. So they have to put some energy there. Yes, I think developer experience is top of mind. It's quite a hot topic these days, right? So many publications around it. So definitely simple object oriented design or simple software design, right, will help a lot in terms of developer experience as well. Because code that is easily understandable, gives a lot of pleasure and creates flow whenever you work on that software.

So for people who love this conversation, they want to check out with you online or they want to buy the book, is there a place where they can find you? For sure I'm on Twitter X my name and surname all together. Feel free to add me on link 10. My website mauriceandnishi.com has a lot of information there, including a link to the website of the book. Those are the main ones for sure that I use. All right. Thanks again, Mauricio for this second episode. So I'm looking forward for next

conversation that we have. So I'm sure we'll learn. We'll definitely learn a lot of things as well. So thanks again. Thank you the.

Transcript source: Provided by creator in RSS feed: download file
For the best experience, listen in Metacast app for iOS or Android