Washing away code smells
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 132 | |
Author | ||
License | CC Attribution - NonCommercial - ShareAlike 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this | |
Identifiers | 10.5446/45003 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
EuroPython 20182 / 132
2
3
7
8
10
14
15
19
22
27
29
30
31
34
35
41
44
54
55
56
58
59
61
66
74
77
78
80
81
85
87
91
93
96
98
103
104
105
109
110
111
113
115
116
118
120
121
122
123
125
127
128
129
130
131
132
00:00
Machine codeSoftwareConditional-access moduleLocal ringLevel (video gaming)Local ringVideo gameSelf-organizationDigital photographySoftware engineeringOnline helpBitReal numberVideoconferencingComputer iconComputer animationMeeting/Interview
01:18
Software developerService (economics)Machine codeLine (geometry)Uniqueness quantificationCode refactoringPhysical systemSurfaceRootSeibel, PeterSoftwareFunction (mathematics)Functional (mathematics)Multiplication signHierarchyArithmetic meanWeb pageUniqueness quantificationMachine codeComputer programmingCode refactoringResultantProduct (business)Process (computing)Home pageLine (geometry)Goodness of fitWindowMultiplicationAreaTwitterBitService (economics)Point (geometry)Message passingLevel (video gaming)Standard deviationCategory of beingGroup actionSound effectInheritance (object-oriented programming)Physical systemBuildingSocial classOffice suitePrice indexRootParameter (computer programming)Software developer2 (number)Projective planeLattice (order)System administratorComputer filePrime idealControl flowSpacetimeSurfaceRight angleBoom (sailing)Computer animation
10:19
Machine codeCode refactoringChi-squared distributionModule (mathematics)Function (mathematics)Variable (mathematics)NumberTelephone number mappingEmulationConditional probabilityElectronic mailing listParameter (computer programming)Social classSet (mathematics)Data structureRight angleElectronic mailing listIntegerMultiplication signCorrespondence (mathematics)Parameter (computer programming)MereologyRight angleSubstitute goodDefault (computer science)Category of beingTupleFunctional (mathematics)Point (geometry)String (computer science)Social classVideo gameSoftware developerLevel (video gaming)Data managementModule (mathematics)Computer programmingCASE <Informatik>Dependent and independent variablesSingle-precision floating-point formatMachine codeMaxima and minimaElectronic program guideSinc functionSpacetimeData structureProduct (business)Task (computing)Revision controlSound effectCode refactoringInformationMorley's categoricity theoremData dictionaryTouch typingNumberCache (computing)2 (number)CountingAttribute grammarGroup actionBitHand fanType theoryMathematicsMessage passingSoftware bugVariable (mathematics)Goodness of fitTelephone number mappingEnumerated typeComputer animation
19:20
Electronic mailing listSet (mathematics)Library (computing)Software testingCode refactoringProcess (computing)Execution unitMachine codeWritingDisintegrationImplementationControl flowSoftware maintenanceProduct (business)Task (computing)Metric systemCountingFunction (mathematics)Line (geometry)TupleLine (geometry)Machine codeComputer programmingThumbnailMessage passingCountingRule of inferenceCode refactoringOnline helpLibrary catalogProduct (business)Default (computer science)PressureFunctional (mathematics)EstimatorSet (mathematics)Projective planeCompass (drafting)Multiplication signSoftware testingData managementCASE <Informatik>ParsingMereologyBoolean algebraDirection (geometry)Open sourceBookmark (World Wide Web)Different (Kate Ryan album)Goodness of fitLibrary (computing)MathematicsINTEGRALBitElectronic mailing listExecution unitProcess (computing)Data structureWritingLoop (music)Semiconductor memorySoftware maintenanceTask (computing)ResultantSlide ruleSoftware bugImplementationMetric systemPairwise comparisonWindowBranch (computer science)Social classCodierung <Programmierung>Domain nameRight angleExtension (kinesiology)Computer animation
28:21
Multiplication signMachine codeOffice suiteDemosceneWeb pageGoodness of fitForestCASE <Informatik>Context awarenessTriangleCrash (computing)Code refactoringSoftware developerState of matterRight angleComputer animation
Transcript: English(auto-generated)
00:04
Thank you for coming to the talk. So I'm Yenny, and today I'm going to talk to you about washing away cold smells. So this is a nicer picture of me than real life, and I'm originally from Hong Kong. I'm a software engineer at Yelp on the BizNational team in Hamburg, Germany, where we're scaling advertising tools and also doing reporting
00:25
solutions for our biggest customers. I was previously a speaker at PyConDE, PyDays Vienna, and also the Talk PyCon podcast. The reason why I want to mention this is because I learn a lot from these kind of experiences, so I just want to give credits for the people
00:41
I talk to, the organizers, and also a lot of things that I learned through the experience of presenting too at these occasions. So yeah, a little bit more about my company. Our mission is really simple. We want to connect people with great local businesses. So you're in this beautiful city of Edinburgh, and one of the really important questions
01:03
you ask yourself is where do we get dinner, right? So Yelp helps you find exactly that through people's reviews, photos, videos, and also their opinions on which one you should go to, and that's one of the things that I'm thinking about as well. And this is probably what our home page looks like here. So there is a search, and
01:24
you can just put down the category that you want to search for. So pretty easy. And some fun facts about our company. So Yelpers have written over 155 million reviews. There are a lot of users, as you can see, a lot of monthly unique visitors, and our engineering team is growing very fast.
01:44
We have over 500 developers now over the offices of San Francisco, of London, and also of Hamburg where I'm working. And we have over 300 services, and our model is Yelp main has over 3 million lines of code. And why am I bringing this up to all of you? It's because when you have so
02:03
many people working on the same code space, it's actually really important that we keep a standard of what kind of code we're checking into production. And that's why I want to share with you this little message that I learned basically from my time at Yelp here, and hopefully you'll like it. So that leads us to our agenda. We're going to talk about what code
02:23
smells are, why is that something that we should spend time in, why should we care about this, why should we spend our engineering effort into clearing out these code smells, and how we can use the technique of refactoring to wash away these code smells. And also, if you're sold about the message that I'm bringing to you, how you can bring refactoring
02:42
and this technique to your company. So let's get started. What are code smells? A code smell is a service indication that usually corresponds to a deeper problem in the system. So one thing to note is the service indication here. So sometimes you can have a code smell,
03:04
but maybe there's nothing wrong with it. If you look deeper into the code, if you do some investigation, maybe that's what it takes to complete the problem. So this is the service indication. So I want to bring forward a metaphor here. It's like eating cheese, right? Sometimes you smell really, really pungent cheese and you think there is a problem
03:22
with this, but then when you eat it, well, it's actually fine. It's good cheese. So that's something like code smells. So why do we care about this? So the tech lead for Twitter's engineering effective groups once said, let 1,000 flowers bloom, and then rip 999 of them out by the roots. So what do we mean by that?
03:46
I think that's a metaphor for your developers have a lot of ways to do things. And yeah, of course, everybody can try out, you know, their creativity. We can try out things and see if it works. But then gradually through experiments, we realized that some of them don't.
04:01
So that's the time when we want to rip them out from your code base. And this is why we should do that. First of all, when we just leave it unchecked, it builds up tech debt. So what that means, it's not like if we procrastinate and not solve this problem in this project, it's going to go away. It's actually going to gradually build up and comes back to you in the next project, which is actually
04:23
exactly what is happening to me right now. So there's a problem. And also, it's not like if you design the code, like say, I'm very good at designing code, I do it right the first time, and then it's going to go away. So code actually gradually rots as time goes by. So one example that I can give you is say,
04:41
you have a code base in Python 2.7. You can create everything that's of the standard of the time, but then over time, your code rots, and now we have to migrate to Python 3 because there are new standards. There are new experiments that people do that make things better. So here, we don't want to let your code rot. And there is also a class of saying that talks about,
05:02
why do we need to uphold that kind of quality in code when, you know, one day we'll throw the code away because technology moves so fast, and we're building so many products, so many new products, we might have to get rid of the old products. So why do we have to maintain the standard? Actually, this is a very strong argument on why we have to spend time doing this.
05:23
It's because only with good code can you identify what to throw away immediately. So I can give you an example. I'm sure in your company, there is this one file, there's the admin code, that is 4,000 lines long, nobody know what it does. And, you know,
05:40
that's not great because when you have to throw away functionalities that you don't want, this is the problem here. You don't want your code to be like CSS. So you have, like, a long CSS file, you want to take out one line, and suddenly your page breaks, and you don't know why. This is not what you want your code to be like. And at the same time, I also want to bring forward that it decreases productivity.
06:02
So I definitely have that experience before. I'm working on legacy code. Well, in my dry run, I keep on saying legendary code, so I have to be very careful here. Legacy code. And, yeah, I thought I figured it out this one day. Okay, I know what's happening. I'll even try to write down, you know, how it inherits 10 hierarchy in the Python code.
06:23
And then the next day, I sleep and I come back to work, and then, what is this? So imagine that times five, because your whole team is struggling and doing the same thing. So that really weighs our productivity here. And there is one story that I want to share with you briefly. So in New York City,
06:40
there is an area with a lot of primes, and, you know, the city is very dirty, and there are a lot of drug problems, homeless people. And then they actually sent in some psychologists to check, you know, what is the origin of the problem of why is it so dirty? You know, why are there so many problems? And actually, it all originated from a few broken windows. Can you imagine?
07:02
So if you leave these broken windows, or, you know, and here, analogically, your code smell here, you're giving other people a message that you can just do whatever to the code. You can check in code that is not of enough quality. You're checking in code that is not up to the standard. So you don't want your code to be like this.
07:22
And at the same time, I also think it's very important for developer happiness. And why is that? So a show of hands, who here just really likes working on legacy code? Can I say it's not the majority here? Yeah, so you get the idea.
07:42
Maybe that's also important for us to retain people if, you know, your day-to-day work is just cranking out what your Python code of, you know, 10 levels of inheritance means. Yeah, so probably we don't want that. So we have the technique of refactoring that can actually come to our rescue.
08:01
So one thing to bear in mind throughout the talk, it's about changing the design of your code, but not the functionality. So the end result of what is being produced shouldn't be altered during the process of refactoring. So here, let's go through a small example to develop our code nodes
08:20
to see if we can identify some of these code smells here. So I'll give you one second to look through this. I'm not brave enough to do live coding, but this is the best I can give here. So this is a fairly simple program that gets us the cheese we want based on our mood, hunger, and money.
08:42
So there are a few problems here. This is supposed to be a fairly simple function, but it's quite long. No? Let's see what kind of problems we have here. Can anyone in the audience already see if there is any problem? It's missing one more than three. Yeah, that's one of it.
09:01
It's missing tests. Oh, missing tests. Oh, that's an interesting one. We'll talk about that in a second. Multiple return points. Yeah, that's a good point. So let's organize that a little bit.
09:21
I think you basically hit all the points here already. Right here. So first of all, the naming is a little problematic here. So mood bigger than three, what does that even mean? Does that mean you're ecstatic? Does that mean you're sad, angry? There is no way to tell. So if it's not for the comment, we actually don't know what we're meaning here.
09:41
And we're using comments here as a deodorant to cover up for our code smell, right? So the problem itself is we didn't name things correctly. And that's why you need to write a comment to kind of explain what your code is supposed to do. But we don't actually need that, do we? And as you pointed out here, we
10:02
have some dead code that never got executed. So for a simple program like this, it's pretty easy to spot out that this never gets executed. But for, say, like the program that I was dealing with, maybe it's much harder to see what is being run and what is not, especially when you're not familiar with what the code is supposed to do.
10:23
And duplicated code, which we can also extract out, out of the if-else conditions. And also conditional complexity, right? For a simple program like this, we're nested it for three levels, which is probably the maximum we should have. Like if you have more than three levels,
10:40
you really should think about if there is another way to do things. But yeah, let's do a simple refactoring here. So magic, and oh, we have something better. In real life, it rarely happens this way, and you actually have to take some effort to do that. But yeah, let's point out the things that we have improved here. First of all, just want to mention
11:00
there is a nice doc string that tells you what the function does. That's usually helpful in most of the cases. Also, we changed the mood bigger than 3 to is happy. So now you know it's actually happy. And we have introduced something called the guard clauses here to get rid of the complexity.
11:21
So one thing to know is like the top four are the cases where it's not supposed to happen, and the last one is the default case that you want to return. So if I have money, and I'm hungry, and I'm happy, I'll return blue cheese. And also, a bonus, this one is also PEP8 compliant
11:41
for people who like PEP8. Yeah, personally, I don't recommend you to go bug your colleagues and just be like, oh, you're missing a space there or missing an indentation. There are actually automatic tools that you can use that. But I think it does bring value to your company because since we're working with more than 500 developers it's good to have a style guide of how you write code.
12:01
It just makes things easier to read for everyone. And also, it avoids Git merge conflicts. So that's the practical point. So yeah, we have taken a look at a simple exercise. So now we're gonna dive into some of the points that I want to mention for refactoring.
12:20
So just a quick recap of what we've done here for the refactoring. First of all, we named things right. Same for the comments point. We have removed dead code, and we have dried out the duplicated code. And also, we have reduced the conditional into guard clauses.
12:46
So I can briefly categorize these into categories. And these are probably the lower hanging fruits that you can get basically once you start refactoring. There are definitely much more ways for you to do that.
13:01
But we can also investigate into that later. These are the things that I think is easier to do and has the most immediate effects. So name it right. Naming is actually one of the hardest problem in programming. Do you know which three it is? Yeah, naming is one of them.
13:21
Cache and validation, one more. Threading, okay, there we go, three of them. So I'm happy to talk to you about one of it, which is naming things right. So it's a cure for uncommunicative naming. Python itself, it's dynamically typed. So since you don't have the typing information, we want to name things very clear
13:41
so that other people know exactly what's going on here. Of course, I would also recommend pushing for typed annotation. That is one thing that will help. But naming is also very important for Python. And apart from the variable naming that we have mentioned, there is also function and module naming. And module naming is probably more specific to Python.
14:02
So you probably don't want to do something like from yalp.business.bizimport, bizinfo, like something like this that doesn't give people a clear sense of what you're trying to do. And that's just asking for bugs. And at the same time, keyword arguments, that's part of the PEP8 as well, that increases clarity.
14:21
So if you have it in the code that you're using, then you don't have to go back to the original function to check, actually, this one corresponds to mood, or is it hunger? So it just saves you some time and increases clarity. At the same time, I would want to introduce replacing magic strings and numbers with enums. I think that's a very good practice.
14:41
So here there is a short how-to that we can go through together. So one thing that's good about it, so even the is happy is not as explicit as this one. Now you can specify your mood to be exuberant, you can be content, you can be apathetic or melancholic, as specific as you want.
15:00
And you can specify it right in the enum class of mood. And if you're a fan of the being explicit over implicit, so I think this is something that you'll like. And at the same time, it also have pretty good properties, like it supports iterable, and also it's hashable.
15:22
That means you can use it in a dictionary, you can use it as a key. So it can be a substitute for just a pure string or an integer. And that's really great, because consider this situation where this string is just used in like 15 places in your code, and suddenly your product manager wants you to change it into something else,
15:40
which happens very often actually. And then you just have to change one place in the code, and you don't have to worry about typo, because there is just one place. So that's something that is considered a good practice. At the same time, I want to push for also getting organized. So it sounds pretty simple, but it's actually probably one of the hardest thing to do.
16:02
It's a cure for long functions, classes, and param lists. So one thing that this wants to uphold is the single responsibility principle. So your function or your class is only supposed to do one thing. And how do you know that? Actually, after you write the function, and when you try to name the function,
16:21
if you find yourself having a very hard time naming that function, it's probably because you're not following the single responsibility principle, because it should be fairly easy to name if it's only doing one thing. So in this case, you might have to extract some of that into other functions or break it down. There is also decomposed conditionals, which is one of the getting organized method.
16:42
And also DRY, which is don't repeat yourself. I also didn't know that. I had to look it up, I think, in the beginning of when I learned about this. DRY actually means don't repeat yourself. So yeah, one of the example that I want to give here is fixing long parameter lists. I think this I've encountered when I'm doing programming as well.
17:02
And so here, there is a simple example of identify cheese. You know, identify cheese is a hard task. So we need to pass in the country, smell, touch, city, year, taste, a lot of information for the program to decide what cheese it is. But gradually, this list grows, and it gets out of hand. You have what, 20 parameters that you want to pass in?
17:24
That's kind of like an eyesore. So what we can do is we create name tuples that can organize these things together, and you can even add type annotation that kind of groups them into a more orderly fashion. And hence, you can only pass in, you end up passing in only two things, of cheese production info and cheese attributes.
17:43
So and there is also like a, it's a way to document what exactly do you want to pass in. Yeah, name tuples to the rescue. And actually, going from this point, why do I put name tuples instead of other data structures?
18:04
So that's also one thing that we need to think about when we're designing. So I would want to go through some examples to talk through why dictionaries versus name tuples, and also lists versus sets. But you know, like you use these data structure based on your need, but in this case,
18:20
maybe one thing is better than the other. And we'll see why. So here is how it's like using dictionaries, but this is a wrong case or a bad case of using a dictionary. So just one second for you to digest this.
18:40
So we're just doing some cheese math manipulation, I guess. And we're passing in a dictionary as a default value. So I'm passing in cheese counts that consists of brie and blue. And we're just doing something simple like cheese counts blue incrementing by one. But what is happening here that is scary is,
19:01
so what Python's actually doing is just saving that into like a default variable. And in the beginning, you have what you expected, right? Brie and blue, both are equal to zero. But after you call it once, what happens? This actually gets mutated and your default got changed. So depending on how many times
19:21
you're calling your function, your defaults is different. And that's pretty scary. And imagine having to debug this in our monolith code base, I cannot imagine. So this is one thing that we actively try to avoid, putting mutable data structure as a default. That's not recommended. And here is where named tuples can come as a rescue
19:42
because they're immutable. So the same thing, we can actually pass in cheese counts as a named tuple. And here is how you can specify a default for named tuples. So here, if you're just specifying brie equals two, the default knows that blue is equals zero
20:00
and brie is equals two. If you don't put anything at all, then you get blue equals zero and brie equals zero. And what happens if we put this in the previous function of some cheese? It's gonna shout at you. It's gonna not let you mutate the default. So that's better because you realize the mistake before it goes to production
20:20
and I don't know how far I can take before someone finds out. So there's that. And also between using lists and sets. Here is a function that select my favorite cheese from the catalog. So we're passing in two things, the cheese catalog and also my favorite cheese.
20:41
And you're just basically doing a loop to see if this thing is in the my favorite cheese list and we'll return picking the favorite cheese from the cheese catalog. And yeah, everything looks good. We're passing in blue and cheddar into the cheese catalog. And my favorite cheese is truffle brie, because of course, and also blue cheese, right?
21:02
And in the end, you return blue cheese because that's the only thing in the catalog. It's supposed to do what it does. And that's good, except it's a very long function for this because we can do something actually simpler than that. And that's where sets come in, right?
21:21
So right now, how many lines do we use? One, two, three, four, five. Okay, five lines for a one-liner like this. And you can basically just use intersection to come up with the common set between your cheese catalog and my favorite cheese. So it also returns the same result, except now it's a one-liner,
21:41
which is more of a Pythonic way of doing things. But of course, there are some drawbacks between sets and lists. Maybe sometimes sets has performance or memory implications. So that's also something that we need to consider when we're refactoring. But you know, if for like a general use case, the set comparisons are pretty awesome, no?
22:01
It is. And yeah, another suggestion is to check out the standard library. There are actually a lot of gems in there that I feel like I personally also don't spend enough time on it, especially iter tools and collections. If you make something an iter tool, there are a lot of iterable,
22:20
you can actually make use of a lot of toolings that are on it. And also with different collections like sets and other data structures. So I think those are pretty handy. And yeah, testing. So we're missing some tests. So let's talk about testing. A pretty short one, but it's pretty necessary in the refactoring process
22:43
because if you don't write them, maybe it's too late already. So the first part, I would recommend writing integration or end-to-end tests for the code to be refactored. And that ties back to one of the slides we talked about, right? Refactoring is changing the design of your code and not really the end result
23:00
or the functionality of your program. So supposedly, if you write an integration test, they should pass throughout the whole process of your refactoring. And that's also your compass to make sure that you're doing the right thing during refactoring and not deviate too far away from what the program is supposed to do. So it's kind of like a check for myself.
23:22
And of course, you're having fun doing all the refactoring. And after that, we can write some unit tests for the refactored code to make sure that the code is actually correct. So this kind of helps you, gear you towards the right direction in the refactoring process. And I think we shouldn't be lazy with the tests.
23:40
It's a very important part of the refactoring process. So cool. And so if you think that you're so sold about the refactoring idea, like tackling cold smells, how do we sell this to the company? How do we talk to your product manager about this? How do we convince other people to also jump on the same boat?
24:04
The second weapon is code reviews. So I remember when I first joined Yelp, I don't know who told me, but one of the people who told me, I don't know, like a boy scout rule is, you should leave the code cleaner than you found it. And I think that was very valuable advice. Every time I'm trying to push some code,
24:22
even if I'm not trying to change and reinvent the wheel or try to refactor to a very deep extent, I try to make it a little bit better. Say I see a variable that is not named correctly, maybe I can do a little change. I see a test that is not well covering the code, then I add another test to make sure all cases are covered.
24:41
So things like that. It's a culture that we need to cultivate in the company to make this happen. So one thing that we can do is also to encourage refactoring when people are adding code and especially fixing bugs, right? Because something happened to the code and refactoring can probably help with that. There are also other things
25:00
like you can write code review guidelines to tell the code reviewers what to look for, especially when doing code reviews. So these are also pretty good advice that I've heard. And I think the harder part is coming with the product manager because they definitely have, usually they have a different agenda than engineers
25:21
because they really want the product to ship fast and they care a little bit less on the engineering integrity or the engineering quality. One way I would go about that is to break down the tasks and really take maintenance into account. So from my personal experience, as I told you, all right, I'm working on the legacy code base
25:43
and yeah, I have the hardest time trying to understand what is happening there. And afterwards, I have to spend a lot of time to maintain this code base and it actually takes a lot of time that in retrospect, maybe if we have rewritten some of it, if we have refactored more of it, we can actually save a lot of time
26:00
in squashing the bugs afterwards and in the maintenance part of it. So a rule of thumb that I tell myself is four weeks for with refactoring with the maintenance effort and otherwise six weeks because we have to account for the times that we need to get ourselves into the not so well done coder again. But yeah, if all things fail,
26:23
maybe one thing we can do is to abstract out the implementation detail and as a good engineer, you can adjust your estimates to include refactoring and also the test and just say this feature takes X, but that's the last result. Yeah, so far we've gone through mostly
26:43
the manual work of doing refactoring, but there are some things that can be automated, not all of them. I think we have some open source tools or things that we use. One of it is called undead. So that's based on high parsing.
27:00
It can do massive find and replace. So that's something that's helpful because if you wanna duplicate a function or duplicate a class, duplicate a package, you can do a massive find and replace. And on the same note, we also use a debt tracker called Branch Debt and for some of the code reviews, we actually include that into the code review itself.
27:22
So that gives people more pressure. But both the reviewer and also person who writes the code, how much tech that they're introducing in the new code that they're deploying. Some of the example metrics that this tool looks at, including how much no QA tech
27:40
do you put in in the code, how many deprecated functions are you using, or also how many lines are you adding to the monolith you're making because we wanna move out of it eventually. Yeah, so these are some things that can give yourself more pressure to really deal with it now instead of procrastinating it to later or another project that you're working on.
28:05
Yeah, some of the takeaways just to wrap it up. We talk about code smells. Why is it important? How do we use the technique of refactoring to get rid of them? And also some of the tips to bring that to your company if you're sold about the idea of refactoring and code smell.
28:22
Most important of all, if you want to work for a company that cares about code quality that lets you spend time to do refactoring work, we're hiring. We have offices in Hamburg. That's where I work. London and also San Francisco. So we also have a booth down there if you're interested and wanna talk to us, we're there.
28:41
Yeah, thank you. For the questions, let me remind you, actual questions. So who has one? Thank you for your talk.
29:04
So one of the things that hinders me from some time is because many of these tips look like, when you know them, they look like, yeah, great idea. But are people likely to just make up them
29:24
by themselves or do they rather need to learn them? Do you have any experience, like did you see junior developers, for example, looking at such bad code and do they know what to do with it or rather than?
29:41
So you mean how to acquire the skill of practicing your code notes to spot out what is good in a code and what is bad? Yeah, so I'm just wondering what are your thoughts about it? Is it likely people need to learn it or rather they just see what's bad?
30:02
Yeah, I think, so there is the first gatekeeper, which is the code review, right, where I guess more experienced engineers tend to tell newer or less experienced engineers what they have learned throughout their times. But I'm a proponent of, you know, you crash and you burn and you learn. So, you know, next time you'll know
30:21
because you see what didn't work. Any more questions? Why didn't you sit closer? Okay, so my question is related to say that you should leave your code in a better scene than you found it.
30:43
Some could argue that goes against having small reviews because now the reviewer has to see 10 pages of refactor instead of just the actual value added, so to say. So one of the practice I usually use is I see the problem and I usually tackle it in another code review just so because it's a different context.
31:02
But I would recommend following it up immediately. Otherwise, it will be lost in the Jira forest. Triangle. Or, yeah. Or sometimes if you identify these to be good new hire tickets, that can also be the case.
31:23
Anything else? Thank you, Yanyi. Oh, oh! There's a... There's a top over there. Go ahead.