Does your code smell? Does it have a weird fragrance? It turns out code smells are a real
thing and an amazing conceptualization of suboptimal design. This week you'll meet Yanni
Chung who has some practical and real-world advice on using refactoring and Python to improve your
code and wash away those bad smells. This is Talk Python to Me, episode 150, recorded January 31st, 2018.
Welcome to Talk Python to Me, a weekly podcast on Python, the language, the libraries, the ecosystem,
and the personalities. This is your host, Michael Kennedy. Follow me on Twitter where I'm @mkennedy.
Keep up with the show and listen to past episodes at talkpython.fm and follow the show on Twitter
via at talkpython. This episode is brought to you by ParkMyCloud and Rollbar. Please check out what
they're offering during their segments. It really helps support the show. Talk Python to Me is partially
supported by our training courses. Python's async and parallel programming support is highly underrated.
Have you shied away from the amazing new async and await keywords because you've heard it's way too
complicated or that it's just not worth the effort? With the right workloads, a hundred times speed up
is totally possible with minor changes to your code. But you do need to understand the internals and
that's why our course, async techniques and examples in Python show you how to write async code
successfully as well as how it works. Get started with async and await today with our course at
talkpython.fm/async.
Yeni, welcome to Talk Python.
Hey, yeah, very excited to be on here.
Yeah, it's great to have you. I saw your talk, you know, virtually via YouTube, PyCon.de, and that was
really, really interesting. So I wanted to make, give you an opportunity to come here and share
your technical lessons learned from a refactoring with everybody.
Yeah, thanks. It was also a great learning experience for me with that many Pythonistas
there. And I also learned a lot from people's opinions. We had a great chat.
You know, one of the things I think is a little bit counterintuitive or ironic is
probably when you see those presentations given, the person that learned the most from that
presentation is the person presenting it, right?
Yeah.
Because you had to do all the research and the thinking and it's not just what came out,
but it's like the whole experience, right?
Yeah, pretty cool.
So before we get though into the details of all the refactoring stuff that you talked about,
which is really interesting, let's talk about your story. How'd you get into programming in Python?
Yeah, that's a long story. So when I was in high school, I don't actually know programming as a
thing. So I was just really into math and sciences. So yeah, like I like the problem solving part of it.
I even liked physics as a subject. And yeah, like from there, I kind of know that I like the
application side of things because I feel like, you know, by doing those high school problems,
I can solve real world problems. Not really, but that's how I got into it. And in university,
very naturally, I just applied for engineering. And I didn't really think about pursuing software
engineering at all because I didn't know it's a profession, but there's one requirement course
that I need to take for engineering. And that was CS21, Intro to Programming. So after I took it-
What language was that?
That was in Python. So yeah, after that, taking that class, I think Python set a very high bar for me.
So I didn't really get too much into other languages anymore because of how much I liked Python. It's
such a neat and beautiful language, I would say. So yeah, pretty much after that class, I decided to
switch my major into computing science instead, and I dropped engineering.
That's really wild. So it really connected with you and you're like, I found something I like,
better. I think that's an important part of college actually, is I'm always kind of blown
away that people just go into college knowing what they want and they just do that. Because to me,
college was you go and you try a bunch of things and a few things connect and you sort of follow that
path. Yeah. So what kind of engineering were you doing before you switched into programming?
So it's general engineering. We didn't really pick the discipline yet. By the time I had to do that, I already switched out. So I didn't get to that part of it.
Yeah. I think it's a pretty smart thing to use Python as the first language to teach to people.
Because I really feel like in Python, you spend less time thinking about the syntax.
You get to think much more about the logic of the code and computer science itself.
So compared to other languages, which we learn later, like C++, C, which are great because you get more of the happenings under the hood.
I think Python really took all these distractions out from the get go.
Yeah, I agree. I think it's a really good learning language. What surprises me about Python is there's a lot of languages that are kind of simple in that regard, like Visual Basic, like six, you know, the early Visual Basics and things like that.
They were kind of like that, but they would always they would hit this really hard limit as soon as you try to do very complicated stuff with them.
And Python seems to have struck a balance where it's simple in the beginning, but it can grow into larger applications still.
Yeah, it's totally an art how to write good Python code.
Yeah, that's for sure. So what do you do these days?
Recently, I've been leading a project. So I'm a full stack engineer. I don't only code on Python. I also work on JavaScript and React stuff.
So recently we've been working on such a project. Sometimes I could be doing interviewing or mentoring some other people in the company or preparing for a Python podcast.
Of course, of course. And you work at Yelp, right?
Yeah.
Yeah. That sounds like a really fun place to be.
Yeah, I think so. So I work in Yelp and Hamburg. So not in the San Francisco office.
Here we focus more on the business side of things. So selling ads, helping local businesses find customers and things like that, which I find I find it to be very interesting.
Yeah, like I really enjoy being here.
I'm sure. Is Yelp pretty big in Europe? I only know it through the US. When I lived in Germany, I didn't actually use Yelp very much.
There are definitely like some competitors in Europe as well, but it's growing. It's getting its ground.
Yeah. And you actually have more offices in Europe, which is kind of cool, right? London and Hamburg.
Yeah. Well, the good thing about like working in a European office is you get to fly around in Europe, you know, like you get to go to all these like fun European conferences, which is very nice.
Yeah, that's one of the things I really liked about being there is you're so much closer to many amazing places to visit.
Like where I live in Portland, it's three hours to Seattle to the north.
It's 10 hours to San Francisco to the south, and it's eight hours to Boise to the east.
And those are the three closest cities. Like you're in Paris in two and a half hours, or you're in Vienna or wherever. It's really just such a different experience there. So enjoy it. That's cool.
Yeah. But like the tech scene, I would say it's like very much concentrated in the US. Like say SF, you have such a huge technical community. But in Europe, it's definitely more spread out. So you need to like fly from one place to another.
Maybe like a bigger tech hub would be Berlin. But still, it's really, really widespread.
Yeah, that's an interesting observation.
Cool. So let's talk about your topic that you covered at your talk, which is refactoring. And I think refactoring, you know, it's interesting that we've had programming.
I don't know, like what you might consider programming, maybe since the 60s, right? Do you consider punch cards? I don't know.
But we've had programming for quite a while. But refactoring as an idea didn't really come out until, I don't know, was it early 90s, mid 90s, something like that?
With the whole Martin Fowler thing? Yeah.
Yeah.
So it finally got sort of formalized an idea. So maybe just really quickly for everyone listening, because I know people kind of use refactoring and just changing your code to be better.
And maybe adding features and whatnot while you're doing it as interchangeable. So maybe just quickly give us like a definition of refactoring.
So code refactoring is pretty much the process of restructuring existing computer code.
So that's pretty much meaning changing the refactoring without changing its external behavior. And it also improves the non-functional attributes of the software.
So from the outside, it should appear as if it didn't change, but you might change the way the algorithm is implemented or something to that effect, right?
Yeah. Pretty much changing the design, like improving the design of it, but without changing the functionality of what your software is supposed to do.
Book that kind of announced that was Martin Fowler's book. And you talked a little bit about that in the internet talk as well.
And I just looked it up. It was published in 1999. I mean, that's like height of dot com boom days, right?
It seems pretty far down the line for when that came into existence.
Yeah, but this is kind of like, I don't know, the Bible for refactoring. In a lot of ways, I feel it's like a start of discussion.
But in a way, a lot of those principles can still be applied to other languages and in other situations.
So I would really like to use this book as like an introduction to what refactoring means.
But later on in the podcast, I think we'll also touch on Python might not need that many of these refactoring patterns.
Right. So one of the things that was interesting, you know, so there was a lot of stuff that came around the same time.
So Martin Fowler's refactoring concepts, we had Kent Beck and I think maybe Martin Fowler was also participating in that with sort of extreme programming, paired programming.
We had the whole design patterns in the Gang of Four book.
There were some really interesting foundational ideas brewing in that time.
And certainly the design patterns thinking.
And it seems like just from looking at Martin Fowler's work, that it's very much based on languages like Java or Smalltalk or these heavily object oriented sort of object only languages.
So I think it's interesting to compare that back to Python because it doesn't always make sense or there's problems you'll find in Python that don't appear in the list of, say, the code smells, which we'll talk about or the refactoring techniques because it's fundamentally a different way to structure code in Python.
Yeah, I would totally agree with that.
Like a lot of the problems that the refactoring patterns try to solve, I think inherently in Python's language design, it's already solved.
So we can definitely explore into that a little bit later.
Right.
And then at the same, yeah, I think another one, though, so for example, that I think is kind of an issue you might run into is what I don't know, I would call is like a long module or something, right?
Where everything is just crammed into one file and it probably should be broken apart to be more easily understood.
Right.
But that doesn't appear anywhere in the traditional literature because they're talking about things like Java where you just have one class per file.
So if the file is long, that means the class is long.
Right.
But that's not necessarily the case in Python.
I feel like there's like refactoring obviously applies to Python, but some of the traditional history of it is maybe an 80% match.
Yeah, I would agree.
Like a lot of the ideas translate, but like how you achieve that might change because of the language and how to apply that.
Yeah, for sure.
All right.
So let's start by thinking about just why, what are the benefits of refactoring?
So first of all, I think it cleans up tech debt.
And for those who are not as familiar, tech debt tends to build up when you take shortcuts during development.
There might be a better solution, but instead you took like a shorter one so that you can save some developer time.
But these tradeoffs, you need to pay pretty much like pay for your debt later.
You need to spend more time to fix the implementation, things like that.
Or maybe when you have the spec, it's not complete yet or, you know, the products change, the code changes.
So the thing you design right now might not be applicable for the future.
And in this case, refactoring really helps it get into shape again.
But I just think that it's like a good habit to have.
Whenever I write a function, like it's very few times that I can get it right the first time.
So upon like thinking a little bit more about it or look at it the next day, there has to be, you know, there can be a lot of improvements can be done.
You know, maybe you can extract it to the function, name it better, things like that.
And also, I just think that it really saves productivity because I've worked on legacy code before.
And my experience was just, you know, I come to work, I look at all these code and I, okay, finally, I understood what it means.
Okay.
The next day I'll start implementing new features.
And then I go home, like I had a nice sleep.
I came back and then I just forgot what it does.
And the code doesn't really help because it's so convoluted.
So imagine that, but like times 10, you know, the whole team pretty much does the same thing.
So if some people can be responsible for refactoring this, then everybody doesn't have to face the same problem.
It's almost like a golden rule of programming in a professional environment is you talked about the Boy Scout rule, which was let's leave the place.
In this case, the code in a better state than we found it every time we interact with it.
So that's a pretty interesting idea.
Yeah.
That was one of the first things they tell us at Yelp.
You know, it's like a common courtesy.
You don't want to hand worse code to your colleagues, you know, than you found it.
So that's where the Boy Scout rule comes from.
Yeah, that's pretty cool.
I think another sort of real world analogy applied to code that would be appropriate there would be the broken window theory.
So like for those of you who don't know about that, that's in, I believe it came out of New York City.
And there used to be, the city was kind of run down and there's a lot of crime.
And they said, what can we do to make these neighborhoods nicer?
And there was some psychologist or something that realized that just a few broken windows and a little bit of decay sort of communicated to the people of that neighborhood that, oh, this place, we don't care what it's like.
It's kind of broken.
And what does it matter if we break it a little bit more?
Whereas if you come upon really nicely factored code that's really clean and beautiful to read, you don't want to go in there and mess it up, right?
Yeah.
The expectation is if I'm going to contribute to this, this is beautiful.
I'm not just going to throw junk on it, right?
So I think that's an interesting psychology about it as well.
Yeah, that's a very good analogy.
At the same time, I like what Martin Fowler mentioned in his book.
He said he's a lazy developer.
So the fact that he's working on this code right now means that it's very likely for him to work on this in the future.
So just refactoring right now makes sure he can understand it easier in the future.
Yeah, yeah.
Yeah, a lazy developer is a good developer.
Productive laziness.
Yeah, exactly.
Yeah, so you said also, in addition to cleaning up technical debt, it'll actually save you productivity now and over time, right?
Yeah, I would agree.
Because after refactoring, supposedly your code should look simpler, increase the readability.
So in this case, the code can also be more usable and easier to maintain.
It's good for you.
And this is the good thing about Python.
Python already reads very nicely, I would say.
Like semantics is very easy to read.
So if you can write good Python code, your code pretty much self-document.
And that's a huge win.
Imagine an organization like Yelp, right?
We have over 300 microservices.
And then we have even like a monolith of code base.
There is it's impossible for you to write documentation to cover every line of what you write, right?
So in this case, if you can write code that is readable, that is easy to read, then onboarding new people.
If they if you have like a new hire and intern in your team, then they can immediately jump on.
Like it really, I think it really brings a lot of value to the team.
This portion of Talk Python to me is brought to you by ParkMyCloud.
The last time you parked your car, did you leave it running?
No?
Well, then why are you leaving your cloud resources running?
Every year, $13 billion are wasted on cloud instances that no one is using.
Don't let any of those be yours.
ParkMyCloud automatically identifies and eliminates wasted cloud spend, saving you 65% on AWS, Azure, and Google's cloud.
You're up and running quickly with a 10 minute setup and no scripting required.
Plus, govern users and easily integrate into your DevOps process.
See why ParkMyCloud was chosen by McDonald's, Unilever, Fox, and more.
Start a free trial today at parkmycloud.com slash talkpython.
So you talked about these 300 microservices and some monolithic code as well.
How do you guys think about, say, refactoring from one larger service into microservices?
Or maybe the reverse, maybe taking, say, five microservices.
These are really not doing enough to be independent things.
We're going to smush them back together.
Whenever we build new features, definitely, it's always in the back of our mind.
Can we make that into a separate service?
Can we modularize it a little bit better so that we don't have to contribute code to the giant monolith?
But yeah, at the same time, you know, if you have more microservices, there is always overhead.
So it's really a decision that is, you need to, like, find a good balance between it, I would say.
Yeah, for sure.
There's definitely trade-offs there.
Another benefit to refactoring, I think, is maybe overlooked.
I'm not entirely sure how often, say, managers take this into account in business, like sort of decision makers.
But it seems to me, as a developer working at a company, if you always work on code that seems to have no care, no craft, it's just kind of thrown together and not well factored.
You might decide either you'll be unhappy or you might just leave.
And so maybe you are actually losing your best developers because they don't want to work with the crappy code.
And so refactoring it into, you know, for all the reasons you already talked about, cleaning up debt and stuff, it seems like it almost could be like an HR issue.
Yeah, for sure.
Like, who doesn't like to work on clean code, nicely designed code?
Exactly.
Like, I've certainly worked on some projects where I'm like, wow, this does not look fun to work on.
Like, why do I, what is even going on here, you know?
Like, crazy variable names and huge stuff crammed into one function or a store procedure or something, right?
Yeah, for sure.
Yeah, another benefit, maybe, what do you think?
Flexible software?
I think there are different thoughts into how you should write software because some people think that, you know, software is kind of like a throwaway thing because products evolve so quickly that, you know, your code probably doesn't live very long.
Like, after one year, two years, you have to rewrite it.
Things come up and then, you know, you look at code that's supposedly to be very well designed before.
But, you know, two years after it's not, you know, code tends to rot.
And so, one thing about refactoring is the thing that came out from refactoring should be easy to modify.
So, I think refactoring actually complies with this thinking, you know.
If it's easy to read, you know exactly what to change.
You know exactly what to throw away.
And that's very important.
Yeah, absolutely.
And so, a lot of the refactory stuff also applies to breaking things into smaller, more well-understood pieces, right?
Instead of one huge log function, maybe, like, a couple of functions and, like, a class that represents the data or something.
So, it actually just makes it easier to evolve and change over time anyway.
Yeah.
Like, a counter example would be CSS.
You know, you can only add lines to it and not really take it out because you don't know, like, you know, once you take out something, it breaks.
So, this is exactly the situation we want to avoid.
That's a really good way of thinking of CSS.
And I definitely noticed that because you're like, well, but what about that one page that nobody realizes there?
Or there's that admin section that you don't want to redesign that.
So, you know, I find, like, the changes go into, like, the bottom of the CSS file.
Or if it gets really out of control, like, you'll create, like, this page overrides those CSS settings and we'll include that file after this one, right?
Yeah, I think it's, like, a nightmare.
But, like, recently I'm working on a React project, which makes it better because you have CSS modules.
But, you know, again, these theories of, hey, like, modularize your code can apply to every language, even CSS.
Yeah.
Do you use anything like less or SAS or just straight CSS?
Yeah, we do use SAS as well.
But I think the good thing about CSS module is you can declare, you know, this element is what needs this class.
So, in a way, you can know exactly what is used and what is not used.
Right.
Yeah, yeah.
The dead selector stuff is really bad.
It's even worse than regular code.
Yeah.
So, that's sort of the benefits of refactoring.
When do you think we should refactor?
Like, what are some of the best times in sort of the software lifecycle?
I think, yeah, that's a very good question.
I think it's really an iterative process.
So, whenever you add code, you know, remove code, or when you're understanding code, when you're fixing bugs, those are always very good timings for it.
And another thing I would also want to point out is during code review.
So, if after this podcast, you're more into this refactoring things during code review, then maybe you can comment, hey, actually, you can do this better.
And you can raise the code quality for your entire team.
And, yeah, I would really want to push for the value of code review.
I think part of this might be, like, deciding what's important during code review, right?
Like, are you reviewing for performance?
Are you reviewing just for our code standards?
Are you reviewing for correctness?
Like, this sort of cleanliness aspect of refactoring probably would make a really good checkbox in the review process.
Yeah, definitely.
I've also read up some articles on how to do code reviews and stuff.
And your suggestion is perfect.
I think it's, you know, assigning one reviewer to review that part of the code or that aspect of writing code is very helpful.
Because, yeah, it can also come with one problem with refactoring is, say, if your whole team is all, like, very enthusiastic about refactoring.
And you can imagine how much ideas people can have, you know, asking you to rewrite the code.
And it can also get into not so great of a situation because you'll have a lot of iterations, right?
One suggestion can be having several people in the design, like, involving several people in the design phase.
So come up with something.
And then one person can be responsible to work with the person who's writing code to do refactoring.
That tends to streamline the process a little bit.
I think also there's probably what you might consider, like, micro refactorings versus we're going to change the whole architecture, right?
Like, I have some long function.
I broke it into three and I changed the parameters to be more clear versus, oh, this actually should be another service.
We should use queuing here.
And this, let's make the database NoSQL.
And, like, that's kind of, like, that's another level, right?
Yeah, I think that's definitely, like, a do-later.
Let's follow up on that.
Exactly.
But that's the thing about, like, refactoring on a legacy, a system versus a new project.
Definitely, it's much harder when it comes to a legacy system, especially code that you're not familiar with, code that some other people wrote.
There are so many things that you think you can improve.
But how do you, you know, when do you start and when do you stop?
You know, it's important.
You can't just, like, it's a black hole.
You just continue to refactoring until the end of time.
So I think it's important to time box yourself as well.
One suggestion will be, say, like, this week I spent one day on this.
And I'll wait a bit, you know, like, I'll learn a little bit more about the code.
And then next week I'll come back again and do another day of refactoring.
Yeah.
And you could probably do a couple of interesting things.
You could probably run things like Flake 8 against it or other.
There's probably not just pure refactoring problems.
There's probably a host of problems.
And so, you know, you could approach it sort of, I'm just going to make it a little bit better every day until it's not so bad.
Right, the Boy Scout rule.
Yeah, exactly.
Apply it to this.
One particularly tricky challenge with legacy systems, I think, you know, this typically happens at larger organizations, is there's some not very high profile project.
But it's somehow really important that if it stops working, it's going to be a big problem, some kind of back-end thing or something.
And the person who created it is either no longer on the project or left the company.
Right.
It's written in, like, you know, Python 2.5 or some old thing.
And it's not currently your problem, but you would like to make it better.
But you know if you break it, it's your problem all of a sudden, right?
Like, if you try to make it better and you break it, you now own it because it was working and you're the one who made it not work.
And who even knows how to deploy this thing again, right?
That can be a real quick challenge.
That sounds like a government problem.
Yes.
Yeah, I've definitely seen this at some big companies.
Yeah.
There's actually a book I want to give a quick shout-out to about this legacy system in particular called Working Effectively with Legacy Code by Michael Feathers.
And he has some really interesting ideas of how to basically take a huge existing system and partition off little parts you're going to change and make them testable, flexible, refactorable without overwhelming – without trying to, you know, boil the ocean and change everything all at the same time.
So it's pretty cool.
A lot of techniques there.
Oh, yeah.
I'll definitely check it out.
Yeah, it's really cool.
Unfortunately, a lot of it is sort of C and Java, but the ideas in there are really interesting.
I mean, some of them are so insane.
It's like we're actually going to change the way the linker works to trick the system to do certain things consistently while we're making other changes in C, right?
I mean, it's like really quite far out there.
But you're like, oh, I didn't even think that we could take it that far.
There's a lot of – I'm sure people will get good ideas from it even if it's not in Python.
Yeah, for sure.
Another problem that I think you run into around this kind of improvement but no features has to do with selling this idea to your manager.
And I think while a lot of the modern software development methodologies are really nice, like Scrum, for example, the concept of a sprint in, you know, like a two-week or a month-long sprint, you know, you're going to sign up for some work, right?
Like, well, where does refactoring fit?
Like if I'm already fully booked on time, how do I go and say I'm actually going to do only half as much because I'm going to make things better?
Like, well, we actually just need new features.
This is really important.
So forget the better.
All right.
How do you have that conversation, do you think?
Yeah, I think it's a very important conversation to have.
But the fact that you're thinking about refactoring, you know, like when you're adding new features, you know, you sense that something is wrong.
That might be an indication that we need to do it right now.
So I think the way to communicate with a product manager or with your engineering manager is, hey, if we don't do the refactoring right now, it's actually going to take six weeks.
But if we do the refactoring, you know, spend one week on it, it'll be easier to add feature.
So four weeks.
I think that usually communicates the idea across.
Yeah, I find that does work sometimes.
What I've done in certain circumstances where it was like, look, we're just really busy right now.
We just need to go fast and we'll deal with it later sort of mentality.
It wasn't super as explicit as that, but where that's kind of implied, you know what I mean?
Where it's clear that the people would much just rather have that feature right now.
But that was if that seemed like it was always the case, you know, it's sort of like if everything is urgent, then nothing is urgent.
It's kind of like that.
Right.
There is also the tech debt thing, right?
Like, you know, we need to deal with it later if we keep on building out this tech debt.
You know, it's not like we don't have to do it.
Never.
It's going to come back to you.
It's going to haunt you one day.
Yeah, absolutely.
I feel like that will work well in a place like Yelp where it's a pretty technical company.
Let's say I'm just going to completely make up a company that I don't really know whether they're technical or not.
But let's say I work for like like a food production company that makes like cereal.
Right.
Like those managers probably don't know or care about what technical debt is.
They just want like the new feature for their website or something.
What I found in those situations, I was just start adjusting my estimates.
To include refactoring and testing instead of saying, well, I'm going to spend this much time on the feature and this much time on refactoring, this much time on testing.
I'm going to say this feature takes X.
Exactly.
If I'm going to work as a professional developer, that means refactoring and cleaning up technical debt and putting in tests.
And you ask how long it takes, this is how long it takes.
Right.
You just don't say it's done.
Right.
You're giving a professional opinion.
I totally agree with that.
Yeah.
So I think you've got to adjust maybe per like what kind of environment you're in.
But it's, it is a little tricky to say, I'm going to take a bunch of time and do nothing in terms of what you see that we get.
I'm going to not do anything.
Right.
But of course, it's actually making it much better for all the reasons we talked about.
Yeah.
Yeah, exactly.
Yeah.
I do think that your, your warning earlier was really interesting though, is because there's an absolute possibility that you just like go refactoring and pattering crazy and just go like, all right, we're going to keep changing this and keep changing this.
And I always see this, like it could really be unended.
So I guess one of the things that might be interesting to talk about is like, when do you know that you should refactor and how should you go about that?
Yeah.
This is a very interesting topic.
So in the same book, actually, Ken Beck coined the term of code smell.
So pretty much means it's a surface indication that usually corresponds to a deeper problem in the system.
So I would like to make a metaphor with cheese.
Sometimes like, you know, the key word of this is the indication.
So sometimes cheese can smell very strong, you know, especially those like French cheese.
And then like you think there is something wrong, but when you eat it, it's actually good.
So code smell is an indication of a problem.
But you really need to take some time to investigate into it if, you know, it's actually a problem.
So you have like a very long function, but it actually does one thing.
So, okay, perfect.
This concept of a code smell is absolutely just like captured my imagination when I first heard of it because it's so perfectly captures what is wrong.
It's like if you look at some code and your nose kind of wrinkles up, you know, like, oh, look at that.
Like if it works, it's actually working just fine.
But to get in there and to be with it is a little unpleasant.
Like that is just the perfect idea of this code smell, right?
Yeah.
Well, that's the thing.
Like, you know, any programmer can write things the machine can compile.
But then only good programmers can write code that, you know, humans can read.
Yeah, absolutely.
And so I think what's interesting about the code smells is it's not just like, hey, there's this idea of smelly code, but there are actually smells, flavors of smell, like types of smells that then are prescriptive of different refactorings, which is super interesting.
And that's kind of what we were talking about at the beginning where some of the smells are more applicable to, say, like Java than they are to Python.
But still, there's plenty of Python analogies here.
Before we get into the code smells, one thing that also Martin Fowler talks about that just I think is so perfect is he talks about the idea of code comments being deodorant for code smells.
Yeah.
I really like that analogy.
So if, you know, if you have a lot of comments on your code, then that probably indicates that you didn't write it very clearly.
That's why you need to write comments to explain yourself.
So it's important to know that, you know, when you write a comment, you should address why you're writing this, but not what you're trying to say.
If it's a what problem, then maybe you should rename it or try to use some variables to explain what you're trying to do here.
I think this might be the most important idea of this entire code smell thing is literally every time I go to write a comment, I stop and go, why am I writing this comment?
Is it really that I should just stop and rename the function?
Is the function badly named?
Are the parameters badly named?
Is the function too big?
And so I could break it into smaller pieces.
So each one can then be really clearly named because right now it seems like the name would be a paragraph, right?
All of those types of things.
Right.
Exactly.
Yeah.
And people all the time try to fix these with code comments.
And it's just like, just delete the comment, make the variable name three characters longer, but understandable.
Right.
Exactly.
Totally agree.
That's awesome.
All right.
So maybe take us through some of the various code smells and how we might fix them.
Sure.
I can like vaguely categorize them into different classes.
So first you have long and complex code.
You have useless code, coupled code and inappropriate naming.
I think we can go through them one by one.
So let's start with the long and complex code.
Sometimes in your program, you can see very long functions and classes.
That might be an indication that your class or function is not doing one thing, just one thing.
So, you know, that violates the single responsibility principle and the drive principle, you know, don't repeat yourself.
So in this case, maybe we can extract the function or classes so that everything is encapsulated well.
When you're doing this, like when you're using this technique of extracting functions, though, beware of pass by reference versus pass by value.
It's like this mistake everybody makes and you pass in a dictionary rather than just viewing, just getting it.
You're modifying what is inside and, you know, the list dictionary, there are mutables.
It's very dangerous.
It's evil.
Yeah.
Yeah, you do have to be careful about that.
I think this is probably one of the most common things you run into is just something started out small and it grew and it grew and it grew and nobody wanted to really mess with it.
They just wanted their feature in there.
And so they added a little bit more, you know, another if clause, another conditional or whatever, right?
Yeah, adding new keys to your dictionary, like you don't even know when you like pass by, you passed in the dictionary and in the end, the dictionary totally got muted.
And yeah, it's very hard to keep track in this way.
Yeah, for sure.
And some other long and complex code problems as well, say temporary field.
So that pretty much means you call a function and you cast it into a variable.
But sometimes if you're not using this variable for a few times, you can just call it in line.
Otherwise, it gets really confusing.
And when you're trying to extract functions, those are the things that can prevent you from doing it very simply.
And one other thing I want to point out is the conditional complexity, which I think most developers probably encountered.
Me too.
You know, you just want to write something very simple if else and you ended up having three, four layers of nested conditional logic.
And that's just really, really hard to read.
Yeah, I find that people do that a lot because they, how is the right word?
They're testing for success.
So I'm going to say, I'm going to do a loop.
And then if this thing I want to work on is true, I'm going to go into that part.
And then if this other condition also is true, I'm going to go in it.
You end up like almost scrolling right just to read what the code is doing, you know?
That's where the line limit came in for PEP 8, I guess, you know.
But 79 is very, very strict.
I would have to say, you know, you can probably modify it for your own need.
But that could possibly stop people from writing too long of a code.
Maybe you're trying to encompass too much information on one line.
Yeah, for sure.
Well, and if people are looking for something concrete, you're like, okay, I know this is not good, but what do I do?
Like you can reverse the if statements.
You can do what's called a guarding clause that'll say, if it's not good, either skip this time to the loop or return early.
Instead of if yes, yes, yes, if yes, if yes, if you just go, if no, return, if not this, return, if not that, return.
And then what's flat below is the actual thing you want to do.
That can really help.
Exactly.
Yeah, like totally.
Like guarding clauses is a good way to go.
And in here, I would even value simplicity.
And I would even sacrifice, you know, the shortness for the conditional complexity.
So even being more verbals, having more verbal statements, I think it would help instead of introducing the nested conditionals.
Yeah, nesting is bad.
Yeah, exactly.
I think that's also mentioned in the send of Python.
You have flat is better than nested.
So it's good to remember that.
Just import this if you find yourself three levels deep.
Yeah, exactly.
This portion of Talk Python to Me has been brought to you by Rollbar.
One of the frustrating things about being a developer is dealing with errors.
Relying on users to report errors, digging through log files, trying to debug issues,
or getting millions of alerts just flooding your inbox and ruining your day.
With Rollbar's full stack error monitoring, you get the context, insight, and control you need to find and fix bugs faster.
Adding Rollbar to your Python app is as easy as pip install Rollbar.
You can start tracking production errors and deployments in eight minutes or less.
Are you considering self-hosting tools for security or compliance reasons?
Then you should really check out Rollbar's compliant SaaS option.
Get advanced security features and meet compliance without the hassle of self-hosting, including HIPAA, ISO 27001, Privacy Shield, and more.
They'd love to give you a demo.
Give Rollbar a try today.
Go to talkpython.fm/Rollbar and check them out.
So what are some more in this area?
As we mentioned, the mutable problem.
Using dictionary as a param is pretty dangerous.
Just now we talked about why.
So I would suggest using named tuple because that's the thing.
If I see a function and I'm debugging something, I see it passing in as a param, I'll probably cry a little bit inside.
Say it's just like a location, right?
I have so much imagination in my mind.
Could it be like a latitude, longitude?
Like is it a number?
A string?
Could it be like city, country?
I have no idea.
I need to throw in the debugger.
I need to like try to run the program and see what exactly is inside.
But if you use named tuple, it's defined.
You know exactly what is inside.
And when it comes to Python 3.6, there's type annotation.
So really like no questions asked.
You don't have to guess anything.
I think it's a very good way to go to prevent a lot of the work that has to be done and prevent bugs.
I really like that suggestion.
It's great because it takes this kind of unknown thing and captures it into something that you know exactly what's there, how to access it.
And the type annotation reinforces that that is actually the thing that's going there.
Exactly.
So you don't have to worry about the, because it's immutable, you don't have to worry about it being changed.
So less bug will be introduced.
Yeah, that's true.
Yeah, very, very cool.
Another one that I want to throw in here that does not have a code smell, but I want to give it a name.
I'll run this name by you and see what you think.
You know, you talked about dictionaries and parameters.
That's kind of hard.
When I see a method that takes star args, star star, kwrgs, just like I'll just take anything.
You name it, you don't name it.
I don't care.
Just give it to me.
I'm just like, oh my goodness.
What do I do here?
Like I had such a hard problem switching data centers in S3 because I needed to change like the encryption mode.
It was like the craziest thing with the Bodo API.
And I went to look in the, it was like this.
And I went to look at the documentation.
And one of the kwrgs was itself a dictionary, which only had a name and had no description of what even goes into the dictionary.
I'm just like, oh, how do I supposed to do this?
So my proposed code smell name for those types of methods are starry calls.
Yeah, perfect.
You're coining the term today.
Starry call.
I like it.
Starry call.
There you go.
Yeah, some people call it black magic, you know, the quarks that got passed in.
Yeah, I guess like Python, the structure of Python, it really lets you do so many powerful things.
But at the same time, you really have to be responsible about it because with great power comes with great responsibility.
Yeah, for sure.
Yeah, I understand why these methods sometimes exist.
But I feel like a lot of times people are just like, well, it's easier than just like making people name the orders and we'll just let them put whatever and we'll figure it out.
It's like, yeah, but that doesn't actually help them use it.
You know what I mean?
Yeah, exactly.
So the next section is useless code.
Right.
So as you mentioned, too many comments is definitely a deodorant.
Definitely we should write down why, but not what you're trying to do.
If that's the case, then you probably should consider renaming or explaining it.
One very common one is just the duplicated code.
So yeah, adhering to the dry principle, you should extract functions or considering an inheritance.
Well, in Python, you have other ways to do it as well, which we might cover later.
It's the composition pattern, which might work a little bit better in Python's case.
And sometimes you have dead code.
Well, because your code is not modularized, it's very hard to tell if your line is being executed.
But there are some IDs that are smart enough to tell you if the code is not executed or if some variables are not used.
So that can be a good help.
Or sometimes you have lazy classes.
So you have this one class that doesn't really have any functions, only have some fields.
Yeah, like pretty much some fields.
And in this case, you can just replace it with a named tuple.
And that just makes things easier because maintaining classes takes energy, takes time.
Yeah.
And actually, named tuples use less memory than I ever give a class anyway.
So it's probably slightly more efficient.
Yeah, I would agree.
Yeah.
So one tool, these are all great.
And they all can drive me crazy.
Like I've spent untold hours getting hold of some project, looking at some method, going, I don't understand how this is working in this environment.
Like it really seems like this doesn't work.
And so I'm trying to understand this code.
And then it turns out that like after a lot of piecing stuff together, like, oh, the reason it doesn't seem to have any effect is because it's never called.
Like, oh, my gosh.
Oh, no.
Right.
Yeah.
You know, no, it's just so frustrating.
It's a little bit of the broken window syndrome.
It's just like people left it in there, but they were afraid to take it out.
Right.
Yeah.
The CSS.
Yeah, exactly.
It's like the CSS problem.
The other one I want to give a shout out to, which is really just like it's so delightful,
is in PyCharm, you can open up a huge project.
Let's say it has like 100 files.
You don't have to select anything or do anything.
You can just go to a menu.
I can't remember where it is.
But you can say find duplicate code and it will compare like blocks.
It'll just go, oh, this sort of test here is done actually in 20 places.
You could just make that a method.
Yeah.
And that's pretty cool because you don't have to guide it.
You just say go find the duplicates and it'll somehow like put that all together.
I love all these like tools that can help us refactor code.
It's awesome.
Yeah, for sure.
So what's the next section?
So we'll talk a little bit more about coupled code.
So you have something called the message chains.
Pretty much means function A calls function B and then function B calls function C.
So when one thing changes, say in the chain, right, function C changes, there is a ripple
effect.
So everything has to change.
All your functions have to change.
And for functions that have this message chain problem, it's very, very hard to test.
So in this case, you know, the productivity is just drained away from first writing the function,
understanding the function and then writing tests, which is really not great.
Yeah, that can be one of those problems where you try to make some small change down at some
lower level and it like cascades through every layer of the application.
You feel like you're changing so many files just in order to like, well, like let's take
an example.
Like I want to add an extra parameter to the creation of an object way down low.
Well, that means the method that calls that has to pass it, but the class that calls it
doesn't have it.
So its constructor has to take another parameter, which, and then just creates this like this
sort of combinatorial explosion of like, why am I just doing this everywhere?
This is crazy.
Right.
Yeah, I would totally agree.
So if we can flatten it out a little bit, maybe function A can call function B and then
function A can call function C.
That'll make it a little bit better and easier to test.
Yeah, for sure.
Or maybe even there's some other mechanism from getting that information deep down there.
Like maybe it's stored in the database instead of passed or I don't know.
It really depends.
Yeah.
Or create static functions.
Those are always great.
Pure functions.
They're dumber.
It's easier to test.
I don't know what to make the next one.
Indecent exposure.
Oh, it's very clear.
It's exposing your privates to other classes.
That sounds very indecent.
Yeah.
If your class is consistently calling like functions from another class, then it's better
to combine it probably.
So that means you have code that's too modularized in a way that maybe if they share the same
context, they should be put together.
That's an interesting comment because I feel like one of the things that's funny about
refactoring and the code smells is they often have what I want to coin as refactorial inverses,
right?
Like multiplicative inverses, right?
Like there's inline variable and there's create variable.
There's inline method.
There's extract method.
There's push this to subclass.
Pull it down.
Pull the subclass.
Push it to all the derivative ones.
There seems to be like this thing and this undoing thing often in refactoring.
And it really is context driven, right?
Yeah.
That's why I say it's an iterative process because once you have added some code, the situation
is different again.
And maybe what could be code smell before is not a code smell now.
Or if you, upon investigating, the code smell actually doesn't point to anything.
So it really, it's a constant effort, I would say, to keep your code good along the lines.
Yeah.
It's also why you can basically refactor for infinite time because you can do the thing
and then you undo the thing.
Then you do the thing in a different way.
Yeah.
So the last section you wanted to cover in this area was inappropriate naming.
Yeah.
I would love to cover this area because it's one of the three hardest problems in computing,
right?
You have cache invalidation, you have threading, and then you have naming.
That's right.
And this is something I care a lot about as well.
Like it's kind of really closely tied to the code comment stuff and so on.
Yeah.
That I would totally agree because Python is dynamically typed.
So in a way, when you create a variable, it doesn't really have to type information stuck
to it.
And in this case, you know, with great power comes with great responsibility.
We need to name things right because we kind of didn't have this, some extra information
as other languages would have had.
You know, naming variables correctly, naming modules correctly.
One thing you recommend are keyword arguments or at least calling functions in the keyword
argument style, right?
Definitely.
So in this case, you don't have to have like 10 terminals open to see the function definition
when you're stumbled upon this code, right?
Immediately, you know what is being passed into the function.
So I think it's just a more efficient way because it's always good to be explicit than implicit
by the sense of Python.
Yeah, for sure.
Another thing that I think can be challenging is sort of implicit values or magic values.
See what I mean by that?
It's like you had an example around a function that took the mood of a person and the mood
could be like one, two, three, or the sets of numbers.
And is three good?
You don't know, right?
Like it's really hard to understand that stuff, right?
Yeah, I would totally agree.
Like it's definitely like indicating the direction is important, right?
So mood bigger than three.
What does that mean?
Is it happy?
Is it sad?
So casting it into a variable called is happy equals mood bigger than three can have a
wonderful effect of documenting your own code.
For sure.
And Python recently added enumerations as well, enum classes.
And if there's only four moods, you know, making that an explicit enumeration.
So there's like a sad sort of blase or like hair, like then there's happy, super excited, right?
Like it would be really clear that way.
And that'd be a pretty good refactoring too.
Yeah, for sure.
So this is all well and good.
And I would like a better list of Python code smells to guide us, but there's still plenty
to work with from sort of existing, existing literature and writing and stuff.
So how do you go about developing your code nose?
I definitely agree on reading literature on this, but I think it's just a skill that you develop
over time.
So if you actually just look at some legacy code, you can find a bunch for sure.
And just seeing it day to day, you know, like just read more of that and try to refactor
more of that.
I guess we can learn, you know, at which point we need to refactor this.
Is this an indication of a problem?
It really comes with experience.
And also the code review process can help you learn from other people in your team or, you
know, whoever is reviewing.
Yeah, I think the code review process is super helpful if at least that's being incorporated
to the code review.
Yeah.
If you're working more on your own and, you know, there's plenty of people who work even
in companies, but there's, they're kind of more or less on their own.
They're not in a technology company.
That code review can be more or less absent.
And so one of the things that came to mind while you're thinking about this is like, or
you're speaking about this to me is, you know, there's all these code smells.
You could take like one code smell a week and say, all right, I'm going to work on the conditional
complexity problem this week.
So anytime I'm writing code, if I see, I feel myself writing like that fourth indentation
level, I'm going to like remind myself to apply this refactoring or this, this, this technique.
And you could just take them one at a time, right?
Cause they seem overwhelming altogether, but they're pretty simple by themselves.
That's definitely a good idea.
One of the dangers of refactoring, I mean, it's extra high in legacy systems, but in
general, it's a danger is that your intention is to refactor code, but your actual outcome
is you've changed code, right?
It behaves differently.
So what's the role of testing here?
I think testing is very important in the workflow.
So what I would suggest is writing integration tests first for the code you're about to refactor,
if it's not present yet.
And then during your refactoring process, you know, after you extract the function, after
you change the variable names, run it over again to check if the functionality of your code
base has changed.
So that really helps you to limit loss.
You know, like after you change one thing, it's very easy to spot out what has been changed.
And after refactoring is done, you can start introducing unit tests to test that the functions
you've introduced actually uses the right logic.
So with this workflow, I think we got our ground covered.
Yeah.
That sounds like a good, a good way to do it.
And what test frameworks use like the built-in one, use pytest, use Nose?
Yeah.
At Yelp, we definitely have a built-in one, but there is pytest as well, which is very, very
similar.
You can just assert things.
You know, there are a lot of inbuilt functions that you can use.
Also, one thing that Python is great is you can use mocks.
So that's, you know, if you have a network call that you don't want to actually make during
your testing, then you can mock things out.
And that's really, really convenient.
Yeah.
Very nice.
So maybe let's talk about some of the tools that you could apply here, because in Martin
Fowler's 1999 book, he literally shows you the manual steps at every level.
Like, okay, here, first you create this variable, you put this piece here, and then you do this
step.
And it's really painful, but there's more we can do these days, right?
I do think so.
So one thing is, you know, the styling part of it first.
So we have PEP 8, right?
PEP 8 is a Python enhancement proposal that talks about, it's essentially a Python style guide.
So how you're supposed to structure the white spaces, how do you comment things, how do you
use the string quotes, things like that.
So that there are a lot of tools that can help you.
There's the PEP 8 tool, there's Flake8 that can also help you check conditionals as well, I believe.
Does it check for things like dead code and stuff like that as well, like unused parameters
or methods not called?
Yeah, that's big.
Yeah, or PyLint.
And there are a lot of things that we can use that can be programmatic about it.
I would want to mention Raymond Hedinger's talk.
He was mentioning how PEP 8 can become a nightmare because you just have someone from the team
that bugs everyone about, hey, your trailing comma is off, you know, your white space is off.
But you know, there are actually tools that can do that.
And also, you should PEP 8 onto thyself, you know, not to bug everyone in a team about it.
It's like a style guide.
And it should surf like one, right?
Because if at the same time you're pissing off your colleagues or if readability is not
improving because of this style guide, then maybe it's not worth it to do it in the first
place.
Yeah.
And you mentioned it is beyond PEP 8 talk.
That is really quite insightful.
It's a good example of how just following strict rules can lead to actually less readable code.
Whereas if you let it slip just a little bit, but you're creative about it, there's actually
better ways or maybe even not so much that you break PEP 8, but that PEP 8 is not the end.
There's actually more important stuff to think about than PEP 8 about how you structure code,
whether it's more Pythonic and the way that it works.
It's a really good talk.
I think it's done during PyCon as a PyCon talk beyond PEP 8.
One thing he also mentioned is rather than thinking too much about PEP 8, maybe you should
think about the problem P versus NP.
So, you know, like silence.
What is the P?
What is the NP?
It's Pythonic versus not Pythonic.
That's right.
Because you could write perfectly PEP 8 compliant, very non-Pythonic code, or you could just think
beyond it and actually make the code better.
Yeah, it's a good talk.
And it was at PyCon, I believe, in that.
So there's a video version, which I'll try to add in the show notes for people.
Yeah, I really like that talk.
Yeah.
So maybe that's a good place to leave it for the refactoring stuff directly.
But this was super interesting to talk about.
Thanks for sharing your thoughts on that.
One thing I did want to point out to people, folks ask me often about like where to get
jobs and how to get into Python.
Maybe they're doing C and they want to like move over to a place that actually does Python.
So you at Yelp, you guys are hiring, right?
Definitely.
We have so many empty seats to fill and we really want you to be here.
We use a Python backend.
We have over 300 microservices and we have millions of search requests per year.
There are a lot of interesting projects, you know, data for you to play with.
So definitely we would love to have you here.
Yeah, it sounds like a fun place that cares about code quality and craft.
Very nice.
Yeah.
And we have offices in San Francisco or if you want to go to Europe as well, we have London
and Hamburg.
Cool.
All right.
So before we get out of here, let me ask you the two questions.
So if you're going to write some Python code, what editor do you use?
Well, since I'm a full stack engineer, I would actually suggest using Cloud9.
I don't know if you've heard of it before.
So it's pretty much you can code on the browser.
It's super easy to set up.
You can just like get clone the whole repo into whatever environment you're working with
and you can pair programming real time.
And it's just seamless for full stack development.
Good for Python down to CSS.
It's great.
That's really cool.
And I really like the real time collaboration.
It's sort of like Google Docs for code.
Yeah, definitely.
And, you know, it's in the cloud, so you can pick up your progress wherever you left it.
You could code on your iPad if you want.
Perfect.
Yeah.
Yeah, that's right.
All right.
And then notable PyPI package.
Like what's one out there that maybe people don't know about that is pretty cool.
Just now we mentioned PEP 8 or Flake8.
There are pretty cool links that can help you get your quote quality there.
And yeah, I think definitely you should check out the sign of Python because I think it's
pretty much the key rules of this Python language.
There are a lot of things that we covered already, like flat is better than nested.
Simple is better than complex or visibility counts.
But whenever you make a decision when it comes to writing code, then when in doubt, import this.
Yeah, that's perfect.
All right.
Final call to action.
People are excited to get started with refactoring or do it better.
What would you say to them?
Just start right now.
Like you don't need a lot of tools.
You don't need anything.
Start reading materials.
Start learning the patterns or code smells one by one.
I think it's a fun journey ahead.
Yeah, I totally agree.
Thanks for that.
And thanks for being on the show.
It was great to talk to you.
Yeah, thank you.
It was great.
Yep.
Bye.
This has been another episode of Talk Python to Me.
Today's guest has been Yeni Chong.
And this episode has been brought to you by ParkMyCloud and Rollbar.
Do you hear that sucking noise?
That's your cloud provider making you pay for your idle instances.
Turn on ParkMyCloud, plug the leaks, and save money.
Visit talkpython.fm/park to get started.
Rollbar takes the pain out of errors.
They give you the context and insight you need to quickly locate and fix errors that might have
gone unnoticed until your users complain, of course.
As Talk Python to Me listeners, track a ridiculous number of errors for free at
rollbar.com slash talkpythontome.
Are you or a colleague trying to learn Python?
Have you tried books and videos that just left you bored by covering topics point by point?
Well, check out my online course, Python Jumpstart, by building 10 apps at talkpython.fm slash
course to experience a more engaging way to learn Python.
And if you're looking for something a little more advanced, try my Write Pythonic Code course
at talkpython.fm/pythonic.
Be sure to subscribe to the show.
Open your favorite podcatcher and search for Python.
We should be right at the top.
You can also find the iTunes feed at /itunes, Google Play feed at /play, and
direct RSS feed at /rss on talkpython.fm.
This is your host, Michael Kennedy.
Thanks so much for listening.
I really appreciate it.
Now, get out there and write some Python code.
I'll see you next time.
Thank you.