#56 - Refactoring–The Discipline for Writing Good Code - Christian Clausen
“Good code should be resilient to bugs. It should make it easier to do the changes that you want to the system. Some refactoring could make it harder to make changes. So, if you guess wrongly the direction of the software, then it can have a negative effect."
Christian Clausen is a Technical Agile Coach specializing in teaching teams on how to refactor their code properly. He is also the author of “Five Lines of Code”. In this episode, Christian explained in-depth about refactoring, when and how we should do refactoring, the components, workflow, and pillars of refactoring. Christian also shared about a few important architectural refactoring, such as composition over inheritance and changing by addition instead of modification. Finally, Christian also shared a few tips for writing quality software, such as the five lines of code rule, the habit of deleting code, and avoiding optimization and generality.
Listen out for:
- Career Journey - [00:04:20]
- Refactoring & Good Code - [00:06:58]
- Refactoring & Testing - [00:10:07]
- Components of Refactoring - [00:14:36]
- Advice to Start Refactoring - [00:16:17]
- Refactoring Workflow - [00:18:21]
- Pillars of Refactoring - [00:22:07]
- Five Lines of Code - [00:25:51]
- Composition Over Inheritance - [00:30:00]
- Changing by Addition Instead of Modification - [00:34:12]
- Love Deleting Code - [00:37:01]
- Avoid Optimizations and Generality - [00:39:38]
- Favorite Refactoring Strategies - [00:43:28]
- 3 Tech Lead Wisdom - [00:45:17]
_____
Christian Clausen’s Bio
Christian Clausen works as a Technical Agile Coach teaching teams how to properly refactor their code. He has previously worked as a software engineer on the Coccinelle semantic patching project, an automated refactoring tool. He holds an MSc degree in Computer Science and has taught software quality at a university level for five years.
Follow Christian:
- Twitter – https://twitter.com/thedrlambda
- GitHub – https://github.com/thedrlambda
- Medium – https://thedrlambda.medium.com/
Mentions & Links:
- 📚 Five Lines of Code: How and when to refactor –
https://www.manning.com/books/five-lines-of-code
- 35% discount code for all products in all formats – podtechleadj21
- 📚 Refactoring – https://amzn.to/35xAAVU
- 📚 Algorithms to Live By – https://amzn.to/3hkRc5N
- 📚 Design Patterns: Elements of Reusable Object-Oriented Software – https://amzn.to/3plKOQt
- Provably correct software – https://www.infoq.com/news/2015/05/provably-correct-software/
- OCaml – https://ocaml.org/
- Lambda – https://martinfowler.com/bliki/Lambda.html
Tech Lead Journal now offers you some swags that you can purchase online. These swags are printed on-demand based on your preference, and will be delivered safely to you all over the world where shipping is available.
Check out all the cool swags available by visiting techleadjournal.dev/shop. And don't forget to brag yourself once you receive any of those swags.
Refactoring & Good Code
-
Refactoring in its purest form just means changing the code without changing what it does.
-
In practice, we almost always have the target of actually wanting to make the code more readable, more maintainable, better to work within something.
-
In Martin Fowler’s book [“Refactoring”], he equates refactoring with making the code better in the sense of maintainability and readability. But it could also just mean that we’re rearranging code without changing the functionality.
-
You could have many attributes of your code that define what good is in your specific context.
-
What seems to be common to most, if not all of them, is that we want our invariants to be as local as possible. Invariant is the stuff we need to keep in our heads while we’re working with the software. And the more complicated they are and the further away they are from where we’re looking, the more likely we’re going to forget them. And then we’re going to make mistakes and introduce errors into the system.
-
Good code should be resilient to bugs. It should be difficult to make bugs, and it should be easy to get started on. It should lead you in the way that it wants to go. So it’s making it easier to do the kinds of changes that you want the system to make.
-
The downside of that is that most refactoring actually also makes it harder to make other changes. And so, if you guess the direction of the software (wrong), then it can have the negative effect.
-
It’s very linked to how well you’re able to predict the future of your software. I always say that if you’re experimenting a lot - if you have something you’re testing, you don’t really know what’s going to happen to it - don’t refactor it. Don’t spend that time because you don’t know yet if it’s malleable. Validate the business case first and then do the refactoring.
-
In the same way, if you have something that’s end of life soon or sun-setting, don’t spend time refactoring that, making it nice, because it’s not going to survive for long enough for it to pay off. All the refactoring should pay for itself in terms of easier maintenance and cheaper maintenance in the future, and fewer bugs, and higher quality.
Refactoring & Testing
-
Testing certainly has its places. What I’m against is, it seems that some people are fanatic about testing, thinking it’s the only way to check code or thinking that if you have tests, then everything will work out. And neither of those are true. There are other ways to guarantee high quality of your code, and also things can fail even if you have tests. So I’m more an advocate of having a more nuanced approach where you actually pick the quality tools that fit the job.
-
There are so many different levels of security and risk, and it’s all about risk management. What is your company’s strategy for managing risk? Is it doing testing? That’s cool. Is it doing like ‘types that cannot go wrong’? Then that’s also cool. It’s a business decision. What’s your tolerance to risk?
Components of Refactoring
-
When there is something that you’re not doing right or that you’re not doing effectively, it’s almost always because you’re missing the culture of doing it, like you don’t have permission or you don’t have funding or something like that. Or you’re not allowed to reorganize the teams.
-
It could be the knowledge that you don’t have. You simply don’t know that something is bad or how something works, or you’re doing the best that you can, but you don’t have the tools to do it. You don’t have the correct thing to help you do this in an efficient manner, which makes it unfeasible in practice.
-
And refactoring requires everything. It requires you to have time to do refactoring. It requires you to have some sort of way to do it safely, some quality measure. And it also, of course, requires the skills to know “How do I actually do refactoring? How do I know that I’m not just making this code worse?”
-
I’ve seen a lot of people, especially young engineers, who just want to make the code as compact as possible. They think it’s really cool if they can do something in one line, and it may be, but that’s unreadable. I don’t even know if it works, so it’s definitely worse for teamwork.
Advice to Start Refactoring
-
First of all, I’ve recommended the source books that I talk about a lot, like the Martin Fowler’s original “Refactoring” book.
-
What I often found was that it’s actually difficult to get started with. It’s very difficult to sit down and read the book, and then put it into practice. Those are far apart.
-
And that sort of why I wrote my book was to make it more accessible. So that you can actually start reading my book and do some good stuff to your code from the next day or the same day as you read something. Reducing the initial cost of learning is important. I think that’s the first step. You need to get started somewhere.
-
-
The second step, you mentioned getting the funding to do it or the time to do it.
-
I think if you have an organization where it’s difficult to get funding, I think the easiest way to get it is probably to do refactoring before you start working on a task.
-
The problem with doing it the other way, where we first do the change, and then do the refactoring, is that in a lot of places, if you estimated that the task takes some time, and then you spend that time implementing the feature, maybe you go a little bit over, they’re not going to give you the time to do the refactoring as well.
-
I love this quote that I have in the book also by Kent Beck, where he says, “First, make the change easy, then make the easy change”.
-
Refactoring Workflow
-
There are six steps to the workflow that I recommend that people use.
-
The reason it should be part of your daily work is, of course, that we know from all sorts of things, Lean and DevOps and everything else, that smaller batches give us smaller errors and lower risk and easier to fix errors. So the more continuously you do refactoring, the smaller the risk that something’s going to go wrong.
-
And you’re not doing like a full two weeks refactoring that’s just bound to go wrong in everywhere, and create merge conflicts with everyone, and everything’s going to go bad. So we want to do it in small batches, and that’s why the workflow is that you start by experimenting at first.
-
A lot of people just start coding, which I find to be counterproductive in a lot of cases, because we don’t know what we have to build. Usually, the ticket that we get or the task is poorly described.
-
I recommend people start by experimenting with what we call a spike. Where they just write some code, it’s intended to be thrown away. It’s not a production code. It’s just to figure out, is this sort of thing what I want to do? We sort of eliminate most of the uncertainty in this space. The first phase is experiment with a spike.
-
Then, once we have that, we usually like to write down the specification. That’s just a list of the things that we’ve just agreed on, to lock something in and say, this, I have certainty in.
-
Sometimes we do this if there weren’t an automated test with something like a behavior driven development or test driven development. You really write down some tests, and sometimes we just write them in the acceptance criteria, and other times, we do other things. I don’t mind how you do your specifications. Just write down what you’ve tested, what your hypothesis were, and what you now know from the user or the reporter. And then you start doing the code.
-
Finally, you have to go back when the thing is done, and check against the specifications. You have a checklist of the things that it should be able to do, or you run your tests, or however you did the specification, you just check against it to make sure that everything is good. And then you do refactoring afterwards.
Pillars of Refactoring
-
The code should lead you to a natural conclusion that should be accurate with what the code is doing. It should have good method names, is an easy way to start.
-
A poor example, I would say, is when you have comments that somehow go out of sync with what the code is doing. Or when you just give something a good variable name or something like that. You’re helping me not having to check a bunch of things around it.
-
An invariant is something we need to keep in our heads that is true about the system, but that the system doesn’t know about. So it’s something in the sort of a meta layer of programming where I know that this property always holds, so I have to keep track, make sure I don’t break it because then somebody else’s code might break.
-
If you have two different places that rely on the same invariant that isn’t checked anywhere in the code, then I might very easily break it in one place and then affect another place. So we want these invariants to have the smallest scope possible. We want them to be local invariants. We want these to be localized. We want them to be as small scope as possible.
-
If we draw a line around the code that we have access to, and we don’t know who is outside of that line. We cannot affect anyone who is not part of this team, or we don’t own the code that’s calling our code. It’s just defining what is our boundary for change, where are we allowed to change something. So it’s just saying that anyone from outside our team should not be able to see whether we refactored our code or not.
Five Lines of Code
-
For most cases that I see in practice, five lines seem to be a pretty good indicator. And I’ve since learned that someone has done a study of how many bugs that were in code, depending on how many lines the method length was the average method. It turned out that five and six are the lowest number for errors.
-
A lot of people pointed that out that it may become less readable actually by exploding up like this 20 line method that had a really nice flow to it, and now we’re cutting it up into four different things and making this tree. We’re actually making it harder to follow from the start to the finish because you need to go through a lot more layers.
-
The way that I use the methods in the book and in practice is that I let the lines of code show me where the methods should be through blank lines or through other structures that I described in the book. And then I export that out to methods, and then I let the methods guide me to where the classes should be through rules, where I then get a much more finely detailed hierarchy of classes that interact with each other. That both actually help the generality.
-
The point is more that I can isolate things. So I use the statements to guide the methods, and the methods to guide the classes, and the classes actually to guide the namespaces.
-
If you do have an editor that’s not very smart, then my old advice back when I was on things like that, I said that every time you go looking for a method, you should swap it with the one right above it so that it bubbles up to the top of the file. And then the things you’re looking for most often will be at the top of the file.
-
Just every time you go look for something, put it on top. Sort of like a stack. You just take it out from anywhere, and put it on top. Because turns out, we look for the same things often, and some things we look for only once ever, and they’ll just go to the bottom of the file.
-
But really, just use an editor that has the capabilities.
Composition Over Inheritance
-
The thing with the inheritance is exactly the invariant point that I had before. When you have one superclass and you have two subclasses, these two subclasses now share a common invariant through the superclass. So you have actually created an invariant that goes across two things that you can’t possibly view at the same time. It just makes them dependent on each other, and changing one thing’s interface means you have to change the superclass and interface, and then you have to change the other one.
-
So you’re creating a coupling between these two subclasses and also between the superclass on both of them. This coupling tends to strangle us if we don’t manage them very strictly.
-
There are cases certainly for inheritance.
-
It’s a very common misunderstanding that object in object-oriented programming has to do with physical objects. I think Dan North has reiterated the point in some of his talks that - in fact - the idea of an object in an object-oriented language is like a virtual machine. It can solve a task. It’s a message passing system, where the methods are actually the events, or the messages that it passes to other objects that are themselves, little virtual machines.
-
I tend to think of objects much more like people. I have a person who knows how to handle these specific things, and then I can give him tasks that he will then solve for me and give them back. There are much less about physical objects. And then interfaces then become capabilities of that person, like certifications really. This object knows how to do the method M. Okay, cool. He certified M.
Changing by Addition Instead of Modification
-
When we change code, if we modify it, we are again doing something risky. There is a risk that I’m changing this wrong and breaking something.
-
A different approach to that entirely is to say, well, before I go and make a change, I’ll just take this code and put it within an if-else block, where it’s going to be called. And then I’ll put my new code in the else block. Maybe I even duplicate the original code, make my modifications in the else block. But the original code is still there. It’s intact. And I can still run it through that if block that I have.
-
I can change between them very quickly, and that also means I’ve only added things to this code. I never changed the original code. No modification, just added things. But I’ve changed the functionality through the new parameter that I can pass to run the new code.
-
And then when you start doing that regularly, you realize that it’s actually maybe not the best thing to do this with ifs. If you do feature toggling, then we’re doing it exactly like that, actually.
-
But if we’re doing something more complicated where we have two customers who need two separate configurations of the software, or two separate versions of the software, then we’ll do it probably through a strategy pattern.
-
I would probably say, the most important refactoring in the book [Design Patterns] is about how to introduce strategy pattern in a safe way. It’s taking two pieces of code and then making an if into an interface with some subclasses for each of the cases. And it’s super powerful, and it’s very nice and it means we can do change by modification.
-
There are so many advantages to something like that [feature toggling]. You can merge your code continuously into the branch because it’s toggled off. You can deploy it continuously to production because it’s toggled off. You can release it as a business decision, on a timer if you want.
Love Deleting Code
-
Systems tend to grow over time and become very complex. I don’t think we have a good solution at the moment for how to get rid of code. It seems that our code bases are growing and growing, and it’s an active thing to go and delete something, and it takes time.
-
It seems difficult to argue why that’s valuable. In the same way that it’s difficult to argue why refactoring is valuable. Because people don’t understand that we’re carrying this load on our back every time we need to go and check something. If I need to check, as I said, the system before, if there’s an invariant somewhere, I need to check the whole code base. I can’t do it in most cases because it’s growing out of control.
-
I think that’s also part of why microservices had such a huge impact. We’re making the full code base just this service instead of the whole monolith.
-
The way to sort of get through that is by constantly cutting off anything that’s not pulling its own weight. Anything that’s not providing value, whether that’s documentation, or bad tests, or actually running features sometimes.
-
I think it’s a big problem right now that we don’t have a structured way of getting rid of old or dead code or very underutilized code.
-
If you have a legacy system, for instance, you actually wrap it in a different class that you can then monitor how often this (is) actually called. And it also helps if you want to start deleting it.
-
The type system will help you find all the occurrences where you’re calling this and redirect them, or just to see if there are no occurrences of the method being called.
Avoid Optimizations and Generality
-
I have a few things that seem natural to people that people start doing, but that are actually counterproductive.
-
One of them is when people try to simplify things so that they are short in terms of lines of code, which doesn’t make any sense to me at all. Because it’s not actually making the code faster, or usually not even smaller. It’s just not very good for teamwork. And that’s actually where the productivity happens and where the practical applications happen. So, optimizing the lines of code is one of them.
-
Then we have, as you also said, is the generality. The problem is, as I said earlier, you haven’t validated that this is ever going to happen. You don’t know that this is going to happen. So there’s no point in this generality. It’s just something that you spent time on, that’s maybe never going to pay off. And even worse, sometimes it actually prevents us from doing some transformations to the code because it’s overly general. And then you can’t simplify it in the same way.
-
Optimization is another one.
-
DRY [Don’t Repeat Yourself] is actually another one that I’m sort of on the fence about. There are certainly cases for DRY. Like there are optimizations and generality, they have their cases. But in a lot of cases where we see people doing it, it’s actually not the right time to do it because DRY is another way to unify.
-
Instead of copying some code, it’s making a new function, generalizing it, and then using that in two places instead. But you’ve now coupled the two places. These places now change together. That might be appropriate, but it might also just as well not be appropriate.
-
Don’t Repeat Yourself (DRY) is in itself not valuable unless you know that the underlying behavior has to stay in sync, and sometimes that’s true. But without asking the question, you can’t know. You have to stop and think, “Are these places going to change together forever? Or is it actually better to have them be two separate copies of the same thing?”
Favorite Refactoring Strategies
-
The classical one is extract method. That is the thing we build everything on. That’s the foundation of everything.
-
My favorite refactoring pattern has always been and probably will always be the replace type code with classes.
3 Tech Lead Wisdom
-
First of all, it’s about inspirational leadership.
-
Inspiring your people is much more powerful than trying to control anything.
-
So we have to sort of start being inspirational. They have to start talking about things in the right way and getting the team excited to do the things that they want things to go in the right direction.
-
-
To be humble.
-
We’re going to run into situations that we can’t control and that are going to be chaotic. And just going into this with the mindset of “I’m going to be the best. I can control everything.” is not going to help you at all.
-
It’s much more helpful to just say, okay, these things are going to happen. Some of them are out of my control. I just have to weather that and to stay on course.
-
-
To listen to your people.
-
Usually, the people have their own brain. They’re actually smart people, and they have great the ideas. And just listening to that and supporting them and helping them and empowering them, instead of trying to control the situation a lot, is just a much better way to work.
-
Listen to your people, and enable them, empower them, focus on them. And then, sacrifice yourself a little bit sometimes.
-
[00:00:59] Episode Introduction
[00:00:59] Henry Suryawirawan: Hello again, to all of you my listeners. Welcome back to a new episode of the Tech Lead Journal podcast. Thank you for spending your time with me today listening to this episode. If you haven’t, please follow Tech Lead Journal on your podcast app and social media on LinkedIn, Twitter, and Instagram. You can also contribute and support this podcast by subscribing as a patron at techleadjournal.dev/patron, and help me to continue producing great content every week.
For today’s episode, I am happy to share my conversation with Christian Clausen. Christian is a Technical Agile Coach specializing in teaching teams on how to refactor their code properly. He’s also the author of “Five Lines of Code”.
In this episode, Christian explained in-depth about what refactoring is, where and how we should do refactoring to our code. And he also described the components and pillars of refactoring, including his favorite refactoring workflow. Christian also shared about a few important architectural refactoring, such as composition over inheritance, and introducing changes by addition instead of modification. Finally. Christian also shared a few tips for writing quality software, such as his five lines of code rule and why he thinks five is the magic number, cultivating the habit of deleting code, and the advice when to avoid optimization and generality.
I enjoyed my conversation with Christian, deepening my understanding of refactoring and some of his useful practical tips. And I hope that you will find this episode useful too. Consider helping the show by leaving at the rating or review on your podcast app, and you can also leave some comments on our social media channels. Though it may seem trivial, but those reviews and comments are one of the best ways to help me get this podcast to reach more listeners. And hopefully they can also benefit from all the contents in this podcast. Let’s get this episode started right after our sponsor message.
[00:03:30] Introduction
[00:03:30] Henry Suryawirawan: Hello, everyone. Welcome back to another new episode of the Tech Lead Journal. Today I have a guest with me. His name is Christian Clausen. Christian is a Technical Agile Coach, which is different than the typical Agile coach. He’s actually specializing in helping people to properly refactor code. So that’s why the technical term there in his title. So he previously worked as a software engineer, and even has a teaching experience about software quality in the university. So, as you can guess, today we’ll be talking a lot about software quality, and also in particular, refactoring. Christian is also writing a book called “Five Lines of Code”, which is currently in Early Access Program in Manning. So Christian, thank you for your time. Looking forward to have this talk with you today.
[00:04:18] Christian Clausen: Thank you for having me.
[00:04:20] Career Journey
[00:04:20] Henry Suryawirawan: Christian, before we start, maybe, can you introduce yourself to the audience here? Telling more about yourself, your highlights maybe, or turning points in your career?
[00:04:28] Christian Clausen: Sure. Yeah, I was at university and I always like the software and coding and programming. And I’ve done a lot of that. I also like teaching, so I’ve done a lot of teaching at university level when I could. I was a teaching assistant and stuff. At some point, there wasn’t any teaching position for me to fill, and so I didn’t have anything to do. Being a bit of a self-starter, I decided to just start my own teaching thing, where I would invite students to come and have a two-hour tech talk every week, and everyone was free to talk and to say whatever they had on their minds. But it turns out, computer scientists are pretty timid. So I had to host the first 60 myself to get the ball rolling. And doing 60 weeks in a row, I learned a lot of different things, and a lot of different topics and I got really good at learning, really.
And then, after we were done with university, one of my friends asked me if I could improvise a talk. Having done so many of them, could I just do it on the fly now? There’s only one way to find out. Let’s try it. And so, that’s actually how I came up with the idea for the book. That conversation that followed where I try to illustrate. I wanted to do something with code because that’s where I feel most comfortable, and so I wrote up the example that’s in part one of the book. And then, he was like, “Oh, that was an interesting demonstration”. And I was like, “We’re not done. We’re just getting started. I want to teach you refactoring”. And then we started refactoring this code base, and I came up with these rules that are the basis for the book that I guess we can come back to. But that was sort of how it all started. Then I’ve worked as a consultant a lot and I’ve transitioned from being a developer to being like a Tech Lead. And my fondest memory is of my career from my Tech Lead times. So the title of your show is really appealing to me. And then after that, I figured maybe I should try to go more into teaching because I was missing it a bit. You know, I really am passionate about teaching and about people. And so I went, and I actually taught at University of Applied Sciences and taught software quality. But it wasn’t a good fit for my current patience level. I think I’m too young to go and be a full-time teacher. So I went back, and now I’m a technical Agile coach, as you said. I go and help organizations understand how to do technical practices, as you said, testing, refactoring, all of that stuff, but also the cultural side. How do we manage the people? How do we organize things and DevOps and team topologies and all that interesting stuff. So that’s sort of everything in five minutes or something.
[00:06:43] Henry Suryawirawan: Thanks for sharing your story. I could see you embody teaching code, looking at your cap and also your t-shirt, right? They are like geeky sentences. So I’m really looking forward to learn from you. Please treat this as a teaching experience for the audience here as well.
[00:06:58] Refactoring & Good Code
[00:06:58] Henry Suryawirawan: So you mentioned that you specialize a lot in refactoring code. Maybe just a refresher for those people who may not be familiar. Could you give us a definition of what is actually refactoring?
[00:07:09] Christian Clausen: Yeah, sure. So refactoring in its purest form just means changing the code without changing what it does. That’s the definition I always introduce to people. It doesn’t actually have an opinion on how to use that thing. It’s just saying these two pieces of code are the same. We can go from one to the other. In practice, though, we almost always have the target of actually wanting to make the code more readable, more maintainable, better to work within something. So then we call it refactoring and in my book and also I think in Martin Fowler’s book, he equates refactoring with making the code better in the sense of maintainability and readability. But it could also just mean that we’re rearranging code without changing the functionality. So that’s the basic concept of it.
[00:07:52] Henry Suryawirawan: And you relate this to what you call good code. So what does it mean by good code? Is it like code that is readable and maintainable only? Or is there any other property or attributes?
[00:08:02] Christian Clausen: Well, you could have many attributes of your code that define what good is in your specific context. Some people are doing embedded systems where memory layout is really important or performance is really important. There are certainly domains where good code has different properties. But what seems to be common to most, if not all of them, is that we want our invariants to be as local as possible. Invariant is the stuff we need to keep in our heads while we’re working with the software. And the more complicated they are and the further away they are from where we’re looking, the more likely we’re going to forget them, right? And then we’re going to make mistakes and introduce errors into the system.
So it seems to me good code should be resilient to bugs. It should be difficult to make bugs, and it should be easy to get started on. It should lead you in the way that it wants to go. So it’s making it easier to do the kinds of changes that you want the system to make. Of course, the downside of that is that most refactoring actually also makes it harder to make other changes. And so, if you guess the direction wrong of the software, then it can have the negative effect.
[00:09:03] Henry Suryawirawan: Thanks for reminding that part. Because sometimes a lot of developers are into, yep, let’s refactor, let’s refactor, make it better. But it turns out that the design becomes more complex. The code actually becomes less readable, and also it’s not easy to make further change. A lot of times, actually engineers love to refactor. They think the code will be better. But is there any decision point where you think we should think about doing refactoring? Like what’s the purpose of refactoring?
[00:09:29] Christian Clausen: It’s very linked to how well you’re able to predict the future of your software. I always say that if you’re experimenting a lot, if you have something you’re testing, you don’t really know what’s going to happen to it, don’t refactor it. Don’t make it super nice. Don’t spend that time because you don’t know yet if it’s malleable. Validate the business case first and then do the refactoring. In the same way, if you have something that’s end of life soon or sun-setting, don’t spend time refactoring that, making it nice, because it’s not going to survive for long enough for it to pay off. All the refactoring should pay for itself in terms of easier maintenance and cheaper maintenance in the future, and fewer bugs, and higher quality.
[00:10:07] Refactoring & Testing
[00:10:07] Henry Suryawirawan: Also, a lot of times we associate when we do refactoring, is to actually add more tests, adding more automated tests, unit tests whichever tests that is. Because to refactor and not changing the actual intention of the code, the functionality itself, sometimes it’s tricky, right? And we want to have a fail-safe mechanism to say that, okay, we have a test here. We refactor and prove that the code actually still behaves as what we intended to in the beginning. But in your book, actually you come up with a different approach saying that you don’t always have to come from the automated testing perspective. You can actually do refactoring without that. Can you tell us more about that?
[00:10:43] Christian Clausen: Yeah, definitely. So I think testing can be super useful. I’d like to start saying I’m not against testing. Testing certainly has its places. What I’m against is, it seems that some people are fanatic about testing. Thinking it’s the only way to check code, or thinking that if you have tests, then everything will work out. And neither of those are true. There are other ways to guarantee high quality of your code, and also things can fail even if you have tests. So I’m more just an advocate of having a more nuanced approach where you actually pick the quality tools that fit the job. Like many of the refactorings I would claim most, if not all, of the refactorings in my book are breaking down to atomic steps in a way that make it difficult for you to make mistakes while doing them. Of course, this means that we go a little bit slower while doing the refactorings. But that’s to make up for the fact that we don’t have the test to catch us if we slide by the side. So if you follow the steps very accurately, and in small steps, you’re minimizing the risk of error at each point in the process, which minimizes the overall error rate a lot. So I feel like there are other ways to guarantee that you can have high-quality code.
Particularly, my thesis work at university was about provably correct software. Here I was actually sitting down and doing a proof that the computer could then check and verify that the program I’d written did the thing that I claimed that it did. Which means testing was trivial. You know, you don’t need to test something if you’ve proven it works. You don’t even need to run it. I just knew that it worked. So I’m just saying there are so many different levels of security and risk, and it’s all about risk management. What is your company’s strategy for managing risk? Is it doing testing? That’s cool. Is it doing like types that cannot go wrong? Then, that’s also cool. It’s a business decision. What’s your tolerance to risk? Ever since I wrote my thesis on provably correct software, I hope that the automatic cars, self-driving cars would be proven correct. So that we knew that they didn’t kill people they don’t need to kill. Unfortunately, it’s sort of gone the other direction, doing machine learning where we can’t even look at what it’s thinking.
[00:12:43] Henry Suryawirawan: It’s very interesting thesis, provably correct software, right? I haven’t heard it before. Could you tell us a little bit more about how does this thing work? Is it solely relying on certain practices? Or is it solely relying on tools?
[00:12:56] Christian Clausen: I guess you can prove any program, but it comes very expensive to do in the general case. The specific part I worked with was automatically verifiable proof. So we use the proof assistant or a dependently typed programming language, specific language that was built on OCaml, a functional language where you could then also type in the properties that you wanted your program to have. And then it has some extra stuff to help you do the proof in that program as well, and then it would check it without you needing to run it.
There were some limitations, for instance, you can’t do infinite loops because you can’t prove anything about infinity. I mean, that would be unsound. But it also has a very high learning curve, and that’s in my uptakes, the biggest preventer of using this widespread. But some of the practices, some of the lessons we learned from that are seeping into other programming languages. I think the appearance of Lambdas or high order functions in most languages by now, are certainly helping to bring down the number of errors. The fact that we are now talking in maps and filters and loops that are based on the data structure and not on the primitive operator, for instance, that’s certainly also helping. We’re moving in the right direction. And also something that comes very much from my time working with dependent types, is my love of the type system and the type checker, and you’ll see that everywhere in the book. I rely very heavily on the fact that if I’ve said this as a number, I know without checking it, it’s going to be a number. The more properties you can put into that, the more you don’t have to test because it can never fail. It’s actually in a lot of ways, more powerful than tests. If you can express the same properties.
[00:14:30] Henry Suryawirawan: Interesting concept. So I’ll make sure, maybe if you have the link to your thesis, for people who are interested to learn more about this.
[00:14:36] Components of Refactoring
[00:14:36] Henry Suryawirawan: Also, you mentioned that it’s interesting when you do refactoring that you have to understand the strategy of your company, or your business. And you mentioned, these one of the components for doing refactoring. So skills is definitely one, but there are also culture and tools. Can you tell us more about these three components?
[00:14:52] Christian Clausen: Yeah. The most problems that I encounter as a Technical Agile Coach or as a sort of a DevOps consultant is related to one or more of these three. It’s often a combination of them. It’s when there is something that you’re not doing right, or that you’re not doing effectively, it’s almost always because you’re missing the culture of doing it, like you don’t have permission or you don’t have funding or something like that. Or you’re not allowed to reorganize the teams, could be a big one in DevOps. It could be the knowledge that you don’t have. You simply don’t know that something is bad or how something works, or you’re doing the best that you can, but you don’t have the tools to do it. You don’t have the correct thing to help you do this in an efficient manner, which makes it unfeasible in practice.
And refactoring requires everything, right? It requires you to have time to do refactoring. It requires you to have some sort of way to do it safely. Some quality measure. We can’t do it with our eyes closed. Testing is one of those tools, but it could also be types. So it could also be version control or manual testing or 15 other things. And it also, of course, requires the skills to know how do I actually do refactoring? How do I know that I’m not just making this code worse? I’ve seen a lot of people, especially young engineers, who just want to make the code as compact as possible. They think it’s really cool if they can do something in one line, and it’s like, it may be, but that’s unreadable. I don’t even know if it works, so it’s definitely worse for teamwork. So refactoring is sort of a sophisticated balancing act between those three.
[00:16:17] Advice to Start Refactoring
[00:16:17] Henry Suryawirawan: So you mentioned about the culture of getting permission to do it and all that. There are maybe a lot of software engineers who listened to this that are starting their career. So they are kind of like junior. What will be your advice to them? Because they want to do good job, of course, like producing good code, or maybe working with legacy code where it’s just simply difficult to work with. So what would be your advice to them to actually have the courage to start refactoring?
[00:16:41] Christian Clausen: Well, first of all, I’ve recommended the source books that I talk about a lot, like the Martin Fowler’s original Refactoring book and stuff. I recommended those to a lot of people. But what I often found was that it’s actually difficult to get started with, it’s very difficult to sit down and read the book, and then put it into practice. Those are far apart, and that sort of why I wrote my book was to make it more accessible. I’m reducing the cost of learning refactoring, at least that’s my aim. So that you can actually start reading my book and do some good stuff to your code from the next day or the same day as you read something. You don’t have to spend days mulling it over or doing theoretical exercises. You can practice in the exercises in the book, and then you can do it on your own code immediately afterwards. So already then reducing the initial cost of learning is important. I think that’s the first step, right? You need to get started somewhere.
Then the second step, you mentioned getting the funding to do it or the time to do it. I think if you have an organization where it’s difficult to get funding, I think the easiest way to get it is probably to do refactoring before you start working on a task. So that you’ll be like, well, to make this change, I need to prepare the environment to receive it. I need to go and make this code well written so that I can easier make my change. The problem with doing it the other way, where we first do the change, and then do the refactoring, is that in a lot of places, if you estimated that the task takes some time, and then you spend that time implementing the feature, maybe you go a little bit over. They’re not going to give you the time to do the refactoring as well. So it makes more sense to do it first. And I love this quote that I have in the book also by Kent Beck, where he says, “First, make the change easy, then make the easy change”.
[00:18:21] Refactoring Workflow
[00:18:21] Henry Suryawirawan: Wow, I love that quote as well. And you mentioned that it should be part of our daily routine as a software engineer. So how would this routine look like? Maybe if you can explain to us. Because when we pick up a task, for example, we are working on bug fixing or feature, right? So how should we make this as a routine?
[00:18:37] Christian Clausen: I present in the book. I think there are six steps to the workflow that I recommend that people use. And it sort of built from what I’ve seen people go through, but it’s also not accounting for the fact that it’s probably easier to get funding the other way around. This is from an optimal sort of workflow would look like in a company that is mature enough to actually let developers do the development the way that they say. The reason it should be part of your daily work is of course, that we know from all sorts of things, Lean and DevOps and everything else, that smaller batches give us smaller errors and lower risk and easier to fix errors. So the more continuously you do refactoring, the smaller the risk that something’s going to go wrong. And you’re not doing like a full two weeks refactoring that’s just bound to go wrong in everywhere, and create merge conflicts with everyone, and everything’s going to go bad. So we want to do it in small batches, and that’s why the workflow is that you start by experimenting at first.
A lot of people just start coding, which I find to be counterproductive in a lot of cases, because we don’t know what we have to build. Usually, the ticket that we get or the task is poorly described. It’s maybe even not done by a user or anything. It’s just someone has an idea and then they put it on paper. Maybe they follow sort of the Mike Cohn style " as I need to", or something like that. But it’s still, it’s very difficult to get a hold of. So I recommend people start by experimenting with what we call a spike. Where they just write some code, it’s intended to be thrown away. It’s not a production code. It’s just to figure out, is this sort of thing what I want to do? We sort of eliminate most of the uncertainty in this space. I mean, maybe send it to the customer and say, “Hey, I know this isn’t looking great, but does it function the way that you expect it to?” Or maybe we’re also mocking up some prototype for the design as well, just to get a feeling. So the first phase is experiment with a spike.
Then, once we have that, we usually like to write down the specification. That’s just a list of the things that we’ve just agreed on, to lock something in and say, this, I have certainty in. I know these things are true. Sometimes we do this if there weren’t an automated test with something like a behavior driven development or test driven development. You really write down some tests, and sometimes we just write them in the acceptance criteria, and other times, we do other things. I don’t mind how you do your specifications. Just write down what you’ve tested, what your hypothesis were, and what you now know from the user or the reporter. And then you start doing the code. Now you have the specifications, so now you can start doing the code. However, you want to do that.
Finally, you have to go back when the thing is done, and check against the specifications, right? You have a checklist of the things that it should be able to do, or you run your tests, or however you did the specification, you just check against it to make sure that everything is good. And then you do refactoring afterwards. Because now you have this new feature that has been in the state of flux, where you were moving things around, you’re experimenting, nothing was sure. But now that you’re sure it satisfies the specification, now you can go and say, “Okay, I’m going to make it nice before I share it with my team, because I don’t want to trip up my team. I don’t want everyone else to be prevented by this thing.” So you do the refactoring, and you make it nice, and then you deliver it into production or to the main branch or something like that.
[00:21:41] Henry Suryawirawan: Thanks for sharing the workflow. I think the few key things that I pick up there. To have a specification. You have a checklist of things that you need to fulfill in terms of maybe the requirement or the bug fixing. And then you test it, experiment, maybe ask for feedback from users or from whoever that use that feature. After you prove that the spec works, you do the refractoring and again, at the end, you prove again that the specifications are still met. So thanks for sharing that workflow.
[00:22:07] Pillars of Refactoring
[00:22:07] Henry Suryawirawan: The other thing that I pick up and I really like this thing, you mentioned there are three maybe principles or pillars of refactoring. So maybe I’ll mention it one by one. Improving readability by communicating intent. So what do you mean by this communicating intent?
[00:22:22] Christian Clausen: I mean that the code should lead you to a natural conclusion that should be accurate with what the code is doing. It should have good method names is an easy way to start. Give something a name that actually holds. A poor example, I would say, is when you have comments that somehow go out of sync with what the code is doing. Method names rarely do that, luckily. Or when you just give something a good variable name or something like that. You’re helping me not having to check a bunch of things around it. Because I can see, okay, this is communicating what it’s intended to do.
[00:22:54] Henry Suryawirawan: And from my experience as well, this is also a good reminder for everyone, right? Sometimes the thing that you are working on is not written by you in the first place. But it’s poorly communicated in terms of name, in terms of intent. I think this is also a good reminder for us that you should not be afraid of changing even those things that you did not write, and make it more readable. Communicating more intent, maybe changing the name, would be the easiest example of this. But it could be other things, changing the class names, data structure, and things like that.
[00:23:21] Christian Clausen: Yeah. Exactly. Data structure is an interesting point, right? Because if you have an interface with five subclasses, then that’s also communicating to you, then it’s likely there’s going to be a sixth or seventh. That’s also communicating sort of an architectural intent behind this thing. We’re expecting this to have more subclasses.
[00:23:36] Henry Suryawirawan: The second one you mentioned about the principles of refactoring is actually to improve maintainability by localizing invariants. So you mentioned this in the beginning. Maybe if you can explain again by localizing invariants, what do you mean by that?
[00:23:49] Christian Clausen: Sure. Yeah, as I said that an invariant is something we need to keep in our heads that is true about the system, but that the system doesn’t know about. So it’s something in the sort of a meta layer of programming where I know that this property always holds, so I have to keep track, make sure I don’t break it because then somebody else’s code might break. And the fact that also already suggests where it can go wrong, right? If you have two different places that rely on the same invariant that isn’t checked anywhere in the code, then I might very easily break it in one place and then affect another place. So we want these invariants to have the smallest scope possible. We want them to be local invariants. So, for instance, only this needs to be true within this method. It’s unlikely that I’m going to break it because I can see the whole method. Whereas if it’s some global field that I know have either a one or a zero, then you know, somebody could go and put a two in it from anywhere in the program. And I don’t have the mental capacity to actually look at the whole code base, and see, does anyone ever break this invariant? So we want these to be localized, we want them to be as small scope as possible.
[00:24:53] Henry Suryawirawan: Which leads us to the third principle, which is by doing the first two, you should do it without affecting any code outside our scope. So make it a smaller scope as much as possible.
[00:25:03] Christian Clausen: It also means that if we draw a line around the code that we have access to, and we don’t know who is outside of that line. That could be anyone. If we’re doing a library, then it’s software developers. If we’re doing the front end, then it’s the users. We cannot affect anyone who is not part of this team, or we don’t own the code that’s calling our code. So we can’t change the public interfaces. So it’s just defining what is our boundary for change? Where are we allowed to change something? And it might be to say where we don’t have access. We’re not controlling the database. So we can’t change any of the fields into the database, but we can change the layer right after it enters our control, our boundary. So it’s just saying that anyone from outside our team should not be able to see whether we refactored our code or not.
[00:25:51] Five Lines of Code
[00:25:51] Henry Suryawirawan: I like that as well. So, let’s go back to the topic of the book, which is Five Lines of Code. The first thing that is interesting to me, why five? Why not shorter? Maybe three. Or why not longer? Maybe ten. So why particularly five?
[00:26:05] Christian Clausen: I will get that. It takes a good portion of luck sometimes to be a coach or to be on the forefront of doing something new. And I came up with the five lines because I was looking at the game. The example in the book is a 2D game. And so it takes exactly five lines to do one pass through a 2D array. You have two loops, the thing inside with an if and a return or something like that. I was thinking, it does not make sense to set the limit lower than I can do a pass of the data structure, because then I have to actually split up the two for loops and that sort of breaks the intent a little bit. It makes it harder to see that we’re running through this array. And I’m also saying in the book, if you have another fundamental data structure, if you recommend 3D arrays or 4D arrays, then you should probably use a slightly higher limit and fit it a little bit.
But for most cases that I see in practice, five lines seems to be a pretty good indicator. And I’ve since learned that someone has done a study of how many bugs that were in code, depending on how many lines the method length was the average method. It turned out that five and six are the lowest number for errors. I mean, that’s just pure luck. I didn’t know that when I wrote it. But I still think it makes intuitive sense. And I also think that all the advice I give as a coach should be intuitively easy to understand once you’re told. It can be hard to come up with the idea, but it should be easy when you had it.
[00:27:25] Henry Suryawirawan: Why do you think that shorter code actually leads to a much better code? Maybe less bugs? Or is that even more readable?
[00:27:32] Christian Clausen: Yeah. I mean, that’s a good point. And a lot of people pointed that out that it may become less readable actually by exploding up like this 20 line method that had a really nice flow to it, and now we’re cutting it up into four different things and making this tree. We’re actually making it harder to follow from the start to the finish because you need to go through a lot more layers. And that’s a really accurate point. The thing is though, the way that I use the methods in the book and in practice also, is that I let the lines of code show me where the methods should be through blank lines or through other structures that I described in the book. And then I export that out to methods, and then I let the methods guide me to where the classes should be through rules, like common affixes or something like that, where I then get a much more finely detailed hierarchy of classes that interact with each other, that both actually help the generality, like I can reuse more things. But that’s not the point at all. The point is more that I can isolate things. Then it’s an invariant to three methods, but not the last two. Then I can make a class around those three methods where the invariant is isolated. So again, localizing the invariants. So I use the statements to guide the methods, and the methods to guide the classes, and the classes actually to guide the namespaces, but we don’t do namespaces too much in the book.
[00:28:46] Henry Suryawirawan: So a lot of people actually also like, throughout my experience, looking at short code, yeah, it looks nice, neat. But sometimes some people complain, it’s hard to trace, especially if they don’t use a proper tool. If they don’t use proper IDE, maybe they just use text editor. Of course, this will be cumbersome, right? Where you have to navigate across multiple functions. Any advice for those people?
[00:29:06] Christian Clausen: I mean, I’m just using Visual Studio Code and anything that has an F12 or something that jumps to the definition of a something. Well, just it really doesn’t turn out to be a big problem. If you do have an editor that’s not very smart, then my old advice back when I was on things like that, I said that every time you go looking for a method, you should swap it with the one right above it. So that it bubbles up to the top of the file. And then the things you’re looking for most often will be at the top of the file. Another approach that does the same thing is based on a book, “Algorithms to Live By”. Just every time you go look for something, put it on top. Sort of like a stack. You just take it out from anywhere, and put it on top. Because turns out, we look for the same things often, and some things we look for only once ever, and they’ll just go to the bottom of the file. So it’s the same idea. Put things that you’re looking for often higher in the file. But really, just use an editor that has the capabilities.
[00:30:00] Composition Over Inheritance
[00:30:00] Henry Suryawirawan: Interesting approach. So you also mentioned there are two important architectural refactoring. For people, I think this is quite important in my opinion as well. So two architectural refactoring, whenever we want to make it either like shorter, concise, or even better in terms of maintainability, right? The first one is about favoring composition over inheritance. This was also mentioned in one of the books. I forget what is the title. But can you tell us more? Why not inheritance? Why composition?
[00:30:25] Christian Clausen: Yeah. So the “Gang of Four” book or “Design Patterns” as it’s called. It was the first book that sort of introduced design patterns in large way. I don’t know. That may have existed before then. I wasn’t a programmer. But they were sort of the pioneers of design patterns where they were like, we see these general problems again and again, and there is a right solution to solve this specific thing. And then they wrote down the constraints that were necessary, and then they wrote down the solution, which is amazing. It’s a great way to solve a bunch of problems. The thing is though, in my opinion or my view, design patterns were mostly to improve the architecture of a specification. At least, I think that was part of the idea. You could see these things in the specifying phase, and then you could improve them already there, and then you wouldn’t have the problem when it came to the software. The thing is, those things change a lot. You can’t just write up a thing and then it just works the way you intended. It changes throughout the lifetime of the software, and then refactoring comes in and this is the same thing. Introducing those patterns in a way that doesn’t break the system we’ve already built. And in a way where we don’t need to predict anything in the future. I don’t know where the variability points are going to be of this code. But I can see it after I worked with it for two years. I know where I attempt to do most changes.
So the thing is with the inheritance is exactly the invariant point that I had before. When you have one superclass and you have two subclasses, these two subclasses now share a common invariant through the superclass. So you have actually created an invariant that goes across two things that you can’t possibly view at the same time. It just makes them dependent on each other, and changing one thing’s interface means you have to change the superclass, and interface, and then you have to change the other one. What do you know about the other one? The other one might be another team, right? That is doing something slightly different and you don’t know how to sort of fit that specification. So you’re creating a coupling between these two subclasses and also between the superclass on both of them. It’s just a very bad practice. This coupling tend to strangle us if we don’t manage them very strictly. There are cases certainly for inheritance. The rule in the book says, never use inheritance. But it’s because it’s simpler to remember that way. It’s almost never.
[00:32:35] Henry Suryawirawan: It seems counter-intuitive for Object-Oriented Programming, where the inheritance is actually one of the properties of why you would want to use object-oriented. This seems counter-intuitive to me. A lot of examples also in the books during our university, you have shape and then you have circle and triangle and all that. All this seems to say that, okay, you should probably use inheritance for things that are common to each other, similar to each other, right?
[00:33:01] Christian Clausen: Well, I think it’s a very common misunderstanding that object in object-oriented programming has to do with physical objects. Whereas, I don’t remember who said it, but I think Dan North has reiterated the point in some of his talks that in fact, the idea of an object in an object-oriented language is like a virtual machine. It can solve a task. It’s a message passing system, where the methods are actually the events, or the messages that it passes to other objects that are themselves, little virtual machines. So I tend to think of objects much more like people. I have a person who knows how to handle these specific things, and then I can give him tasks that he will then solve for me and give them back. There are much less about physical objects. They are just properties of something. It’s a completely different thing in my mind. And then interfaces then become capabilities of that person, like certifications really. This object knows how to do the method M. Okay, cool. He certified M. Cool. So it’s a different metaphor, but I think it gives better design to think about it like that.
[00:34:01] Henry Suryawirawan: Thanks for changing the perspective for those people who are still into that mindset of objects as like physical objects, or maybe even like biology, where you inherit something from your parents. So thanks for reminding that.
[00:34:12] Changing by Addition Instead of Modification
[00:34:12] Henry Suryawirawan: Another counter-intuitive architectural refactoring, you mentioned that by changing code, right? You should do it by doing addition instead of modification. This is also sometimes counter-intuitive, but it’s very important principle in my opinion. So can you tell us more about this rule?
[00:34:27] Christian Clausen: When we change code, if we modify it, we are again doing something risky. There is a risk that I’m changing this wrong and breaking something. A different approach to that entirely is to say, well, before I go and make a change, I’ll just take this code and put it within an if-else block, where it’s going to be called. And then I’ll put my new code in the else block. Maybe I even duplicate the original code, make my modifications in the else block. But the original code is still there. It’s intact. And I can still run it through that if block that I have. If I just pass a parameter saying whether I want to run the top one or the bottom one, then I still have both pieces of code. I can change between them very quickly, and that also means I’ve only added things to this code. I never changed the original code. No modification, just added things. But I’ve changed the functionality through the new parameter that I can pass to run the new code.
And then when you start doing that regularly, you realize that it’s actually maybe not the best thing to do this with ifs. Sometimes it is. If you do feature toggling, then we’re doing it exactly like that, actually. But if we’re doing something more complicated where we have two customers who need two separate configurations of the software, or two separate versions of the software, then we’ll do it probably through a strategy pattern. From exactly the book we mentioned before, the “Gang of Four” book, strategy pattern is also something that a lot of refactoring, and I would probably say, the most important refactoring in the book is about how to introduce strategy pattern in a safe way. It’s taking two pieces of code and then making an if into an interface with some subclasses for each of the cases. And it’s super powerful, and it’s very nice and it means we can do change by modification.
[00:36:04] Henry Suryawirawan: Also sometimes you mentioned, right, feature toggling. It helps also in terms of A/B testing or some kind of rolling deployment, where you don’t want to deploy all these changes to the people exactly in one way. So you could probably also roll it back by changing the toggle, or maybe changing the implementation of the interface.
[00:36:20] Christian Clausen: There are so many advantages to something like that. You can merge your code continuously into the branch because it’s toggled off. You can deploy it continuously to production because it’s toggled off. You can release it as a business decision, right? On a timer if you want. You can do it during a big conference event or doing some live thing. You can just go and say, now the code is active and now we have this new feature and it’s super cool, and it looks like magic, which is part of what marketing is about, I think. And then, you can also build A/B testing, as you say on top, and you can just keep growing something like that. You don’t need to do anything. You just put things in the feature toggling system. It figures out whether it works. It figures out whether it’s valuable. I mean, it can get arbitrarily sophisticated at that point.
[00:37:01] Love Deleting Code
[00:37:01] Henry Suryawirawan: So, whenever we add more code, obviously the code grows bigger and bigger, right? You mentioned also we, as a developer, should have this habit of deleting code. So why do you think it’s important for us not to forget deleting code?
[00:37:15] Christian Clausen: You said it yourself. Systems tend to grow over time and become very complex. I don’t think we have a good solution at the moment for how to get rid of code. It seems that our code bases are growing and growing, and it’s an active thing to go and delete something, and it takes time. But it seems difficult to argue why that’s valuable. In the same way that it’s difficult to argue why refactoring is valuable. Because people don’t understand that we’re carrying this load on our back every time we need to go and check something. If I need to check, as I said, the system before if there’s an invariant somewhere, I need to check the whole code base. I can’t do it in most cases because it’s growing out of control.
Then I think that’s also part of why microservices had such a huge impact. We were making the full code base in quotes. We’re making that smaller. We’re making the full code base just this service instead of the whole monolith. The way to sort of get through that is by constantly cutting off anything that’s not pulling its own weight. Anything that’s not providing value, whether that’s documentation, or bad tests, or actually running features sometimes. If I have this feature, that’s super expensive to maintain, but there are two people using it, I might just cut that feature entirely. It’s not pulling its own weight, and it’s important to get rid of that. And I think it’s a big problem right now that we don’t have a structured way of getting rid of old or dead code or very underutilized code.
[00:38:37] Henry Suryawirawan: And also coming back to my earlier argument, right? Some people also tend to be afraid of deleting other people’s code. Any advice for them?
[00:38:45] Christian Clausen: I do in the book suggest that if you have a legacy system, for instance, that you actually wrap it in a different class that you can then monitor how often is this actually called. And it also helps if you want to start deleting it. You have the type system again, as I said, I love that. The type system will help you find all the occurrences where you’re calling this and redirect them, or just to see if there are no occurrences of the method being called. Some editors can help you say this private method is not called. But if you have a public method, it can’t possibly know whether it is called. Because if you’re doing a library, how will it know if somebody else is calling it? But if you know that you’re not doing a library, the editor won’t help you. So you have to go in and actually work with deleting code and getting rid of things. And I do it as much as I can. I delete comments. I delete anything that looks like dead code. After verifying, obviously, that it’s not going to break anything. I delete all of the time.
[00:39:38] Avoid Optimizations and Generality
[00:39:38] Henry Suryawirawan: And hopefully your CI will also tell you if you delete something wrong. The build will fail and you can roll it back safely. And you always have the version control, anyway. Hopefully, everyone is doing version control by now.
You mentioned in the beginning that some people also tend to love simplifying their code into one line, multiple lines, or as short as possible, right? And you mentioned this as an anti-pattern. So, like over optimizing your code, or come up with a more generic framework or approach where you think, okay, this will be useful in the future. Can you tell us more why these are the danger things we should avoid?
[00:40:10] Christian Clausen: I have a few things that seem natural to people that people start doing, but that are actually counterproductive. And one of them, as you say, is when people try to simplify things so that they are short in terms of lines of code, which doesn’t make any sense to me at all. Because it’s not actually making the code faster, or usually not even smaller. But some people like it, and it looks cool, and it feels cool while doing it and saying, look at this code that I came up with to solve this complex thing in just 30 characters. It’s amazing. It’s just not very good for teamwork. And that’s actually where the productivity happens, and where the practical applications happen. So, optimizing the lines of code is one of them.
Then we have, as you also said, is the generality. We also want to find this, oh, I have this function that solves a specific thing. But if I just add these two parameters, and then structure it a little bit differently, I can solve this whole other class of problems that is much bigger. And that seems like it would be valuable. Because then you don’t have to do another function. Next time, you can just call this one again. The problem is, as I said earlier, you haven’t validated that this is ever going to happen, right? You don’t know that this is going to happen. So there’s no point in this generality. It’s just something that you spent time on. That’s maybe never going to pay off. And even worse, sometimes it actually prevents us from doing some transformations to the code because it’s overly general. And then you can’t simplify it in the same way. So if I spot that something is always being called with the same parameters, I just removed the parameters. Just in line them, simplify the code. Sometimes it’s much easier to work with after that, and you’ll never need that generality.
Optimization is another one. A lot of people like to do like, “Ooh, look, I shaved off 0.02 milliseconds”. And it’s like, “That’s cool. Why?” Like, how many hours did you spend shaving that off? “It only took me three hours”. “Okay, cool. But the app takes five seconds to start up.” I mean, you’re doing this thing and it feels valuable, and that’s the danger of it. It feels like you’re doing something good, but in fact, it’s not the appropriate timing to do so.
DRY is actually another one that I’m sort of on the fence about. There are certainly cases for DRY. Like there are optimizations and generality, they have their cases. But in a lot of cases where we see people doing it, it’s actually not the right time to do it because DRY is another way to unify. So Don’t Repeat Yourself is DRY. Instead of copying some code, it’s making a new function, generalizing it, and then using that in two places instead. But you’ve now coupled the two places. These places now change together. That might be appropriate, but it might also just as well, not be appropriate. Don’t Repeat Yourself is in itself, not valuable unless you know that the underlying behavior has to stay in sync, and sometimes that’s true, but without asking the question, you can’t know. You have to stop and think, are these places going to change together forever? Or is it actually better to have them be two separate copies of the same thing? And then when they change, that’s fine. Because they’re not related to each other. So those are like four of my favorite games I called them to bash on. The developers do, because they feel good, but they’re just not.
[00:43:10] Henry Suryawirawan: It’s very interesting to listen to this perspective, because sometimes engineers we tend to focus on some engineering best practices. But actually, some of these are counter-intuitive in practice. Especially in teamwork, like you mentioned. Shorter lines of code, maybe with cryptic name, might be shorter, but it’s actually are counter productive to teamwork and how people understand your code.
[00:43:28] Favorite Refactoring Strategies
[00:43:28] Henry Suryawirawan: Maybe for the last section, you mentioned about atomic changes in the beginning. Can you maybe share some of your favorite refactoring strategies or methods for people who want to get started? So can you tell us some of your favourites?
[00:43:41] Christian Clausen: Obviously, the classical one is extract method. I mean, that is the thing we build everything on. That’s the foundation of everything. If we can’t break up methods and do all these things with methods and generalized, unify, you know, you can do too many things with extract method. I really like it. It’s a stable. A start. But my favorite refactoring pattern has always been and probably will always be the replace type code with classes. Because the first time you take an enum or something like it and then replace it with classes and see how all the code just magically just simplifies. All the ifs go away, all the checks and stuff. It’s just, it’s so wonderful, and it just looks really cool. I was mind blown. Chapter four is, in my opinion, if you don’t get the chills while reading chapter four, then maybe you’re too advanced for the book. You’re already too smart. But that’s just amazing. And as I said, probably strategy pattern is both more useful and more powerful. But I still just love the replace type code thing. I mean, it just has a special place in my heart. That was when I knew refactoring was like so powerful.
[00:44:43] Henry Suryawirawan: So for those of you who listen to this and you want to feel the chill, make sure you read chapter four of Christian’s book, “Five Lines of Code”. When is it going to be out, actually?
[00:44:53] Christian Clausen: That’s a great question. I wish I knew. It’s in production, so we have some people checking the layout and making sure everything looks nice and cool. And then, they send it to me for review, and that’s the process that we’re in right now. We’re reviewing all the chapters, making sure everything’s lined up nicely, and the code looks good on paper, and everything is just good to go. I’m hoping it, but I’m fairly certain it will be this year. So the question is, how long will we take to review all these changes?
[00:45:17] 3 Tech Lead Wisdom
[00:45:17] Henry Suryawirawan: I hope it goes smooth. So, Christian, thank you so much for spending your time here. It’s really a pleasant talk, and I learned a lot from you. But before I let you go, I have this one last question that I normally ask for all my guests, which is for you to share your three technical leadership wisdom for all of us here to learn from. And maybe also look at some of the wisdom that actually matters for us. So can you share us your three technical leadership wisdom?
[00:45:38] Christian Clausen: So since this is called Tech Lead Journal, I’ll talk about how my experience as a Tech Lead was, and the things that I found out to be working for me when I was a Tech Lead. First of all, I would say, it’s about inspirational leadership, right? It’s about actually inspiring your people is much more powerful than trying to control anything. So now when I coach technical tech leads, I usually say that the only power they’re getting is they can host a talk 20 minutes every second week where they talk about whatever they want. That’s their time. But if they’re not inspirational, they’re wasting it. That’s the only way they can control anything. So we have to sort of start being inspirational. They have to start talking about things in the right way and getting the team excited to do the things that they want things to go in the right direction. So that’s definitely the first one. It’s very important. That’s also what I like about teaching obviously, it’s inspiring people.
And the second one, I guess, is to be humble. We’re going to run into situations that we can’t control and that are going to be chaotic. There are going to be errors and outages and managers that are shouting and customers are shouting and developers are crying. There’re going to be so many different things to handle. And just going into this with the mindset of “I’m going to be the best. I can control everything.”, is not going to help you at all. It’s much more helpful to just say, okay, these things are going to happen. Some of them are out of my control. I just have to weather that and to stay on course.
The third thing is sort of the same, but in a different way, is to listen to your people. Usually, the people have their own brain. They’re actually smart people, and they have great the ideas. And just listening to that and supporting them and helping them and empowering them, instead of trying to control the situation a lot, is just a much better way to work. When I was doing one of my favorite assignments as a Tech Lead, we had an issue that we would send back change to review. We would call the customer say, Hey, do you see the change? Do you like it? Is it the way that you want it? And they would say yeah, sure. But I also need. And then they would add something, you know, they just pushed something onto the backlog, under the table, going around all of the processes and everything.
When I came to the Tech Lead there, I was like, so what are you guys working on? They’re like, I’m working on this thing and this thing. I’m like, those are not on the backlog. Why are you working on those things? I can’t see what’s happening. What the team is doing? And so I said to them that from now on, they could not talk to the customer. The customer could only talk to me. Because then the developers wouldn’t get interrupted. They wouldn’t call the developer and say, oh, also I need this other thing, interrupting them from the flow that they were in. Focusing on flow is like the thing, right? You need your team to be productive as much of the time as possible. So I’ve just tended to absorb a lot of the waste into my time. And then the developers could be super productive, deliver great software, and then that would bring down sort of the waste over time. So yeah, it’s listen to your people, and enable them, empower them, focus on them. And then, sacrifice yourself a little bit sometimes. That’s fine.
[00:48:24] Henry Suryawirawan: I’m laughing as you mentioned that story, right? Because this is like a psychological thing, where you show people your good work. And they acknowledged, but they add something, like psychologically. And you become like, okay, maybe let’s just do this small thing and it will be better. So thanks again for reminding this. So Christian, for people who want to connect with you, whether they want to learn more from you, any blogs, any websites, or any Twitter account or LinkedIn for people to look for you?
[00:48:50] Christian Clausen: Yeah. I am TheDrLambda on GitHub, on Medium, on Twitter. Pretty much everywhere developers are, I’m the Dr. Lambda. And I try to reserve that in as many places as possible. So, hit me up. I love talking about code quality and software development and stuff.
[00:49:06] Henry Suryawirawan: Wasn’t aware of that. We could have talked about your Dr. Lambda thing, but maybe for another episode. All right. So thanks, Christian. Good luck with your book. Looking forward to have it on Amazon or somewhere where people buy books.
[00:49:17] Christian Clausen: Yeah. Thanks for having me.
– End –