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.