How do you make refactoring more accessible? Christian Clausen has literally written the book on it - with both practical examples and a sense of perspective.
Welcome to DevOps Sauna. I am Lauri and I am the Chief Marketing Officer of Eficode. One of our people Christian Clausen from Aarhus, Denmark, published a book called Five Lines of Code. In his book, he teaches working developers the shortcuts to quality code. The book introduces Christian's unique approach to refactoring that's focused on concrete rules. I won't spill all the secrets of the book here, so let's tune in and listen Christian tell about refactoring himself. Hey, Christian, how are you?
I'm good. How are you?
I'm doing excellently. How has your day been so far?
There's a little bit of allergies today, so it's dragging me down a bit.
Yeah, it's the pollen.
Yeah. I know what you're talking about. When I was young, I was allergic to the birch, which happened in July.
So, that was a different time. Thank you for taking the time today. I'm happy to have a discussion with you about the book you authored, Five Lines of Code.
Thank you for having me.
Now, for the audience that probably don't know you personally, why don't we start by giving you forward to introduce yourself. Just tell a little bit about yourself, about your professional background, and of course, something that you'd like tell about yourself, unprofessionally or non professionally, whatever you want to say.
So, my name is Christian. I am a Technical Agile Coach during the day where I go and help teams do software better, but focusing on the technical practices like more programming and testing and stuff. I'm trained in software quality and doing functional programming and doing formal proofs and guaranteeing that code is at the highest possible quality. And that has sort of been my focus always to do high quality software. In my spare time, I like to climb and just write books, apparently.
Climbing, there was a recent opening of a bouldering center near to where I live and it's an interesting hobby. And I should say that even though I do trail running and I consider myself be in a good condition, I am humbled by the challenges that as small or less four meters of walk and give to somebody. We are going to-
Yeah. It's amazing.
... Oh, it is indeed. We are going to talk about your book today, Five Lines of Code, and more specifically what's in the book and what motivated you to write that and all of that. Let me start by asking a question that is not answered in the book is, why does the book exist? What was the backstory for you to start to write it?
I've always liked writing and I've always liked books. I collect as many technical books as I can, and I try to read as much as I can, especially technical stuff. And it's a wonderful thing sitting down with a book and then just going on a journey with the author. So I've started countless books of things I've found interesting all through my life.
But this was the first time I had an idea where it was like I started writing it and then I took it out after a year and I read what I've written and it was just... It was an epiphany to myself. I was being like, "Did I think this? This is super useful. These are great ideas." And after I'd done that a couple of times, I was like, "Well, maybe I'm not the only thing that would find this useful. Maybe this can help someone else." And so I contacted Manning.
Yeah. Books have two dimension. One is what's in the book. And then one is the book itself. And I remember one neuroscientist who said that, "When people read books, they sort of form a mental model of the book, which is an amalgamation of the physical object and the contents in the book." And that's why he advised that whenever you are reading nonfiction, then you should be reading it in printed format.
Because, the fact that you are holding a book in your hand and turning the pages, that will sort of mash up with what's in the book, and then you will easily remember later, "Okay, I need to go back and look that stuff up. It was probably one third down the book on the top, right after the picture on the left hand side." It's something that as he argued, you cannot get from printed books by understand, from digital books. But I understand that your book is available both digitally and in print, because I have read that book part of it digitally.
It will be available in print once it's completely done. Currently it's still being written. So, it's only the digital, but I completely agree. I also have that feeling where I know sort of the spatial dimension really adds to a book and to the repeatability. But, for my book though, I will say the most valuable thing you can do with it, is following along yourself. Typing on a keyboard, doing the exercises or following the example, because it is a practical book and its practical skills. So there is no substitute to having done it with your own hands.
Yeah. I think this is a good bridge to our second topic, which is how to apply then the topic, which is refactoring in the daily development. So, what does a refactoring matter in daily development?
Yeah, that's a great question. It's a thing that I discussed with teams a lot when I go out and talk to them about refactoring and about how they can become more efficient. Because, as it turns out, not doing refactoring, is very detrimental to code. If you keep adding couplings, you keep adding a lot of stuff, if you never take time to tidy it up, it'll just keep dragging you down. You're running with this huge backlog of things that are just messy and it can ruin a lot of things.
So, it's important that people actually incorporate refactoring into their daily work and let it become part of solving a task, not closing a ticket before refactoring is done, for instance, is something that I try to tell people. In the book, I describe a six step model for delivering work. And so, it's whenever you get some work ticket or something, you start by doing a spike, where you just try to figure out what am I going to build? What does this need to do even? Is it possible? Just do something and then throw it away afterwards.
Use that knowledge that you then gain from the spike to write down a specification. What am I going to build? And what is the acceptance criteria? When is it good enough? This will usually take the form of an automated test. And then you develop the code using some form of testing maybe, and then finally you test it to the specification, make sure it's up to the standard that you wanted. Then you do the refactoring to make sure that you're not delivering anything bad into the codebase that's going to be bad for the rest of the team. And then finally, there should be some process for delivering the code, pushing it to master or integrating it into a branch or something.
Cool. Very rigorous process. Let's say, I can only imagine what kind of collaboration it's between publisher and the writer to get the book out the door. As a matter of fact, I remember one speech where the writer, the author of the book said that publishers are really good at two things. One is, designing the cover. And the other thing is giving a book a name.
Your book has a very interesting name, Five Lines of Code. I don't know who came up with that, but as I was acquainting myself with the book, I discovered that it's not only a name of the book, but it is not figurative Five Lines of Code, but it is literally Five Lines of Code. So tell us about how did you or who come up with this rules and why that is so important.
Just a note on the publishing. I mean, I love working with Manning. They've been super great. I don't feel like they've held me back in any way or made me do anything. It's all my ideas and they just support me. And it's been great. I came up with the title also, and it is inspired of course, by the first rule. That's the sort of the fundamental rule of the book.
The whole of part one of the book, is going over one example really thoroughly. And it's built to be like this realistic sized program. It's not a small trivial, "Okay. Anyone could do that. It doesn't work on my codebase." It's a big example. It's a few 100 lines of TypeScript. And then we go over how to transform those into something that's a lot more scalable, I would say. A lot more flexible.
And so, in that, the first rule we introduced is, a method should not be any longer than Five Lines of Code. That's called the five line rule and we try to do it... That motivates a lot of the other rules, trying to get down to that limit. Because for methods that are a lot of just single statement, it's pretty easy. You can just extract method. But, once it gets more complicated, you might have an if with more cases than that, or you might have switches or something. It's not easy to see how to actually break that down to less than five lines.
So it's motivating you to figure out creative solutions and to do more advanced refactorings. So it sort of guides you through the whole part one of the book. The five in the five lines comes from... that is how many lines it takes to do a single pass over the data structure that we're working with, which in this case would be a 2D array. Because it's a 2D game. So, it takes five lines to go through a 2D array. If we were doing a 3D game, it would be six lines of code. If you're doing something that has 26 dimensions you can do 27 lines of code, I guess.
I understand. Yeah. Very, very intriguing. I believe it's also, let's say it gives a healthy cognitive challenge for people to try and force themselves to stay within confined limits, whether it is five or six or more. But, the fact that there is a rule that you have to conform to then sort of forces you to, in this case think inside the box.
Yeah. And that's super important. These rules are meant for learning. They're a learning tool. They're not a tool for how to develop perfect software, but it does force you to learn the rules. And then once you've learned the rules, you can sort of start breaking them. You have to learn to do them to learn to break them and figure out when it's right for you and transition to doing code smells, for instance, instead of the rules.
Yeah. Yeah. Speaking of software development more broadly, I have a quote as I was... I need to confess I didn't read your book thoroughly, but I had an opportunity to acquaint myself for a great deal of it. There was a quote, "If you already know how to do automatic testing, feel free to use it throughout this book. If you don't, don't worry." And that was something that stopped me at my tracks in that, it's a little bit of a departure from the typical test driven development.
The purpose of the test driven development is essentially more of part of the technical unit testing work to be parallel with the implementation itself. So, why does the book, or your thoughts behind the book sort of steer away from the test driven development or testing in general perhaps?
So, the point of the book is to let refactoring be available to more people than it perhaps has been so far. A lot of people I've met in industry are simply not familiar with our practice in refactoring, because they don't have time, because they don't have all of these things. One of the reasons then can also be, they don't have a solid foundation in testing. And so, if you meet a codebase with no tests, people ask, "Can you not do any refactoring then?" That's also an issue because you can't do proper testing without refactoring the code to fit testing also. So it's sort of a chicken and egg thing.
And so, for the purposes of the book, which is learning refactoring, you don't need testing, is my thesis at least. You can learn it first and then learn testing at another time separately. And just having the tools, and being extra careful, it is not as safe as having testing of course, but if you've accepted the risk of not having testing during regular development, you'd probably accept it also doing refactoring. Both are risky. So I see testing more as a property of the quality that you've decided to do. It's a strategic decision really, rather than a technical decision.
Yeah. Yeah. I understand. Yeah. This reminds me of some of the recent conversations that I had with our CTO, Marko, who has really, really nudged me into looking into test driven development given that I decided to dip my toes on the Clojure functional programming, being a Perl developer myself in the past. And I can certainly attest to the fact that, being a good tester is a different scale than being a good developer. Maybe that's my personal view and you might disagree with that, but I can certainly see that I have some qualities of being a software developer myself, but I have practically no qualities of being a tester myself. So, what are your thoughts on that?
I think many people actually find it harder to do testing properly than to do a refactoring, for instance. Refactoring is... the first time you learn about moving code around and having it be flexible like that, it's amazing. It's blowing your mind and stuff. But once it sinks in, it's like, "Okay, this just means I can look at code from an abstract point of view and move it around, make it more flexible."
That's super useful even from just doing refactoring a lot on testing projects, that doesn't matter, where you don't need to do testing. That will make you automatically write code in a more flexible manner. I mean, just practicing these things, wax on wax off as the karate kid sensei would say. Yeah, you just write better code from learning it and it gives you a different perspective. That's super useful. Even not having mentioned testing at all.
Yeah. Yeah. Speaking of the... I don't know, putting the opinions out there, there were two other places where, maybe it's a fair to say that you had an unusual position. So there was a comment, if performance is important, it should be handled in a separate phase from refactoring guided by profiling tools or performance gurus. So, you could say that is also a departure from some other schools of thinking.
Why do you think that way?
So, it comes from all the way back from the original refactoring book Martin Fowler, where he has an example for the first, I think, 40 to 50 pages. That's super great. And really, it changed my life reading that. And so, in that he says, "We were splitting up a loop that goes over in one pass over something, to go into passes over that same thing." And immediately I was like, "But you're degrading performance, You're going one pass the two passes."
And he argues that the benefits of having clean code and fast development often outweigh the benefits of having fast execution, especially because, 90% of the time, the execution time doesn't really matter, or you're not in something that's critical. If you're doing something once or twice, it doesn't matter. If you're doing it every second, of course, it starts to matter. And so, people do not have a natural sort of understanding of what the critical paths are or what the hotspots in the code are.
And that's why we need profiling tools if we absolutely need to do refactoring or performance optimization. I also happen to think that they should be motivated by some external need, a performance test or an acceptance criteria, something that we can actually look at and say, "This is why we're doing performance optimization." And then the profiler can tell us where we need to do the optimization. Because, just doing it because it's fun, is not very good for the team usually.
Yeah. Yeah. I can remember two occasions from my own personal experience. One was, one of the very, very old project, little land project, Linux, Apache, my SQL and Postgres, or Perl, PHP, whichever way you want to look at it. You were able to run the whole database in memory because it was so small. So, of course it performs very well regardless of how you have built it. The other remembrance is from the times when I was doing Perl development, about 20 years ago. I sort of found very handy use for recursive function and then my tutor and my senior colleague at that time, he told me a way from recursive function.
So now this summer, I learned that if you were to do any loop processing in Clojure, it's pretty much an imperative to use a tail recursion, so recursive function. You sort of have to learn a way from another conventions as you adapt different ways. And I could imagine that in refactoring, it's also the case that you might have some old conventions that you have gotten used to, and while you're learning new tricks, you also have to let go of the old habits.
Yeah. And not only that. So, breaking up a method used to be very slow. Having one method means it's already cashed correctly and you don't need to think about organizing your functions and stuff. But compilers have gotten good at that. Better than humans, I would say mostly. And so, this is also a factor that changes, programming languages evolve and implement new techniques for optimizing and stuff. I used to always check for evenness by just looking at the last bit.
But, modern optimizers don't care if you do the Modulo-2, that will be equally fast. Because they'll just figure out the fastest way to do this. And that's another reason to have profilers in hand as you do this, because they don't care. They look at the actual time of the execution and so, you know these are the facts that to rely on. And so if one of your assumptions change or is broken along the way, it doesn't matter because you're profiling it.
Yeah. So to a degree, you are handing out something that you used to do yourself into good enough software, so they can help you out in your journey as a software developer. In my past, we used to talk a lot about a mind machine partnership. And it sounds something similar that you have. You have a machine helper to helping you out over those humps, that you had a habit of taking care of yourself.
The third unusual position, I think you call it yourself DRY, don't repeat yourself. I haven't come across that acronym before, which probably tells much about my maturity as a developer. But, you write, in many cases code duplication is bad because it encourages divergence. But in a particular case in this book, this is my interpretation, it makes me feel as if you advocate it code divergent. So talk to me more about the opposition on don't repeat yourself, principle.
Don't repeat yourself is a very interesting topic because people are very, very tied to it and very happy to it. That's part of why I think it's dangerous. It's very, very effective. It's become sort of a target, a hobby or a game to sort of hit down these don't repeat yourself. How much reuse can I get right? These two lines look each other. Can I merge them to become one?
And sometimes that just obscures the code and sometimes it's not what you want. So, really what I'm advocating for is the making an active decision. Whether you'd like some code to have the option of diverging or whether you'd like it to converge. So, in a lot of cases, you do want it to converge, probably most cases. But if we forget completely that there are occasions for not doing unification of code, then we're equally bad off.
So, what I'm mostly advocating for is, don't do unification of code at any cost, consider when it's right and consider what it means. When you have to duplication in the code, I like to call it unexploited structure. There is some structure there you can see it as a human. Why is it not visible to the compiler? Is it because it's incidental and should never be joined? Or is it because it is intentional and should actually be joined?
So I also have a whole chapter on how to unify code. It's not like I don't do it at all. I do it a lot. I just also want people to consider it might be accidental and it might hurt you to unify code. And other case entirely that I see in the industry is when people share some common code across two different teams, and then nobody takes responsibility of that shared code because it's shared.
And changing it becomes this whole thing of processes where you need to communicate with the other team, and there's a lot of overhead, and then nobody does it and it's full of errors, but it's code like that that can fall between teams is also another bad reason for unifying code. In that case, I would rather have both teams just maintain their own copy of the shared code, so that they both have responsibility and have the agility that they need.
Yeah. In the economics, I think there's a very well known term called the tragedy of commons that has probably brought itself into the IT space as well. But it's that, if you have a shared pasture and you have two farms and they both have their own cows and they lead their own cows to the shared pasture, then eventually you will have a problem because either nobody's taking care of the pasture or that it never grows grass because it's nobody's problem in a way.
I could imagine that that discussion you just had sort of slightly relates to refactoring patterns or rules that you really have to take a contrast issues and you have to look for the right approach. So there is the term that you use refactoring patterns or refactoring rules, do you have some favorites that you would advocate people to start with or something that you have found yourself?
Well, so those are really two different questions. To start with, I recommend the simpler ones that are going to give you a lot of easy wins upfront. So, I always recommend the first pattern to people would be extract method. It's probably also the most used in the industry, because it's simple, it can't really go wrong, it's integrated in most IDE, so you can just ask the computer to do it. And that means, again, that can also be a way to be safe refactoring, even when you don't have tests is when you have a machine do it for you. Right?
So, that's sort of the first thing I would recommend. My personal favorite though is, is in the beginning of chapter four of the book where I replace a type code, an enum as it is with classes. That's so powerful and that's so cool. It blew my mind the first time I saw that in Martin Fowler's book. And I was like, "That is important." This is when it's really happening because we're changing something that's low level data to being higher level of control that we can do stuff with. We can pass these objects around and we can do so many cool things with that. So, that's really my favorite refactoring pattern.
Yeah. I remember far too many years ago, I don't even know if it counts as a refactoring pattern, but one was the passing references instead of passing data which really goes back to our performance conversation. That was one. But one more recent I surprisingly stumbled upon that in one article in medium talking about refactoring patterns, which was instead of building functions that rely on the certain order of arguments passed to the function, you should be passing function, let's say a named map. So basically a hash mapping Perl, or you would have a map in Clojure where the arguments are in the named map. And it doesn't matter which order they are because the function can always use the right argument using it's a key word. So, surprising enough, it's like almost everybody can relate to that, but probably not everyone thinks it has every factory in pattern.
Well, it sounds a little bit iffy to me because you're actually... So another big point of the book is relying on the compiler and using the compiler as the tool that it is. It's a power tool. It's super powerful. It can check so many things for you and stuff. And moving stuff into a hash map, will sort of hide the types or the order from the compiler. It sort of opens you up to having, reference not found or stuff like that, which is dangerous in itself.
I much recommend if you have so many arguments that it makes sense to do that, you should probably break up the work into smaller chunks and then bundle those together in objects that are a lot smaller. And by using objects that way, you can actually guarantee that the right argument goes in the right position, always. Right?
By having argument three class that can only go in argument three. You have to do it the right way, otherwise the compiler simply won't allow you to do it.
Wow. Yeah. I'm learning and interesting that we are now here, we are creating a dialogue between two subject matter experts of refactoring. One advocating one way and another one having a preference over the other way, which probably tells that there is no right way to go. There's just the fact that you have to be aware of what you are doing and consider the side effects of whatever route you have chosen.
Yeah. My point of view also, of course, requires that there is a compiler, it doesn't work in untyped languages like Python or Perl. Right?
Yeah. It depends also on the language.
Yeah. Have you come across misconceptions about refactoring?
I have. Especially when I was younger not as experienced as I am now, I came across the thing a lot that refactoring was gold blading in some sort. Where it was, "We don't have time for doing gold-plated code. We just need to deliver features. We need to do work instead." And I wasn't at the time experienced enough to contest that. But the fact that the matter is, it's not necessarily gold-plating, of course, there is a point at which refactoring isn't economically viable anymore, but I think it's way beyond what most people consider time to stop.
It's also an interesting discussion I've had whether code can be perfect. Can you refactor a program so much that it's perfect? I like to say that, it can only be perfect for a direction. It can be perfect for the next change. Because, you need to know where the code is heading in order for it to make sense to refactor it towards that. I would never refactor a code that does need to change. Code that never changes, just leave it as it is. There's no point. That would be wasted time.
So, whenever I need to do a change, I know how the code should look to make that change easy. And that's actually a quote from Kent Beck. That's wonderful for how to start refactoring in a big legacy code. He says, "First, make the change easy, then make the easy change." And I really like that because it shows that it's about knowing where you're going. Otherwise, refactoring isn't really that valuable. But in parts that change a lot, parts that are very hard for changes, then I would definitely refactor that a lot to make all of it very moveable.
Yeah. I can find many, many parables outside software development in that phrase. It's so easy to imagine in what other scenarios other than software development and refactoring, you could apply that. So, I think there's something universal in that statement.
So, how do you then, considering that it almost sounds to me that whoever is on the journey of maintaining an application life cycle, they have to do constant prioritization of whether I should invest my time right now for new features or making sure that whenever I want to pay down technical debt, I'm preparing it to be easy or I'm actually doing it. So how do you prioritize refactoring then, in the daily development?
So, I think it should actually not so much be a prioritize. Well, I mean, it should be a weighing or a balancing because you should do it all the time. After implementing a feature before delivering something, you need to refactor it so that it's not putting an obstacle for the rest of the team. You need to actually go in and make sure the code is in a state that you'd be happy to continue working, but unless you're working in end of life, or doing... well, yeah, end of life, mostly.
I can see that there are opportunities where you need to roll out some hotfix very quickly to get service back or something. And in that case, it's completely fair to postpone refactoring until the system is working again. But then it will be the very next thing for me to do, would be to go in and actually fix it up so that it's not being an obstacle in the future. Putting in these obstacles, even from a completely different perspective, where if you want to do estimates in a codebase, if you want to know how long a change will take to make, the far the biggest constraint on that is how messy is the code in which you're going to implement that change?
If it's super messy, then it's going to take so much longer than if it's super clean. But the problem is, when we do estimates, usually we don't look at the context. We look at the issue that needs to be solved, and then we categorize based on that. But that's usually not the biggest time factor. The biggest time factor is the environment. And so, if you want to do clean estimates, you also need to refactor a lot, to have the codebase be in at least sort of a consistent way. You need to have it be uniformly messy, at least.
Part of that sounds to me like, let's say a teamwork. A lot of stuff that we have discussed up until now can be applied even though if you are individual soldier plowing away. But the bigger the software project, the more likely it is that the software is a product of a team. Let's then extrapolate that to a question, how do you approach refactoring in a team?
So, that's also super interesting topic to discuss especially with teams. I think what's most important is going in the same direction. Because if two people have different preconceptions of what refactoring is or should be, then they might end up going in circles, refactoring back and forth between their two styles. That's neither useful, it's wasting a lot of a lot of time and money. So, the first step is to agree on a standard or agree on the quality constraint or something like that.
And then how to get there, and then developing, what is it called? Your own set of rules that will sort of support that. The reason I like rules is that it enables onboarding of new employees very easily, because rules are super easy to apply. Whereas we can perhaps agree on some smells, but again, smells might leave a lot to interpretation. So we might again disagree on that. So, I think being very concrete and very explicit is important for refactoring as a team.
Right. Now you said rules, and you could argue that sometimes it's easier to automate stuff than make rules happen. So when you think back to the definition of DevOps, for instance, according to some definition, it's the CAMS, where the A stands for Automation. So, why should we not automate the rules then?
So, it might seem like the rules are very easy to automate and they probably are. But the point is, as I mentioned earlier, they're actually a learning tool most of all. They're there to teach you and to force you to learn refactorings and to give you a sense of how good code feels like, then also how it sort of when it doesn't work, that's just as important. Is not necessarily what perfect software should look like, it's more a tool for going down the path and staying on the path until you've learned what makes sense and what doesn't make sense.
It was important for me that I gave people something that they could go out and use tomorrow. They don't need to sit down and do reflections. They don't need to do all of these things. You can read a rule, the Five Lines, then you can go look at production code and say, "I can determine instantly whether this fits in that rule or not." The rules are not perfect for all software they're useful for learning, is what I like to say.
Right. Okay. So, when you have matured, then you can apply the rules instead of just blindly following them? Which probably-
And you can break them. Yeah.
... Yeah. More importantly that. You've referred to one book which you said it changed your world. So, as we are nearing to the end of this conversation, what about the other sort of sphere of refactoring books? How does your book relate to the existing books and the sort of what vacuum does it fill?
So, there's of course Martin Fowler's Refactoring book, which was the one I was recommended and which was my first exposure to refactoring. And I thought that was just amazing. It was a new world looking at code through eyes. And then I was very motivated to learn more about how to do code right. So I'm also am very inspired by Robert C Martin's Clean Code books. All of them in the serious basically, and read Refactoring to Patterns and all of these other books on refactoring.
The thing that struck me about most of them is that when I recommended them to people, they were not super easily digestible. There was a lot of knowledge and a lot of content in them, but it also took some reflecting and some understanding. It took time to read them. They're not particularly easy books. So, what I wanted to do was to make refactoring more accessible. To make a book that you can read before those that will sort of ease you into it a little bit more, by giving you some of the practical perspectives, by giving you some good advice on how to do things. And by putting it in a bit of a bigger example or context than maybe some of the other books do.
Good. How do people find you? So, if people have other questions, if they want to learn more about you and your works.
I am on Twitter, and I also have a blog on Medium, and I have a stream actually, where people can come and watch me write code, or do refactoring or ask questions. All of them are at the Doctor Lambda.
Okay. So we'll put the handles in the show notes so then people who listen to this, they'll be able to follow you more closely. And now, a listener might ask how could they take advantage of your skills? As a matter of fact, the book is out there and we are able to provide a few books free of charge. That's the first thing that the people who are listening to this and they think, "I really, really need to look into that book."
Send an email to firstname.lastname@example.org and we will take the first few ones and we will send you a copy. And then for others who were not that snappy and so quick in writing emails, then we're going to put a discount code out there. So everyone who really feels, like it, they can take a very, very affordable book on that. And finally, of course, your personal skills, you offer Technical Agile Coaching and Technical Consulting through ethics code. So if you are really, really lucky, then probably they get to work with you.
Yeah. I love going out, doing multiprogramming and talking about refactoring, testing with people.
Wonderful. Christian. It was a pleasure talking with you and thank you for your time. I really appreciate it. Very, very interesting discussion. Good luck finishing the book for being printed out there, so people can get ink on their hands when they read your book physically.
Yes. Thank you for having me. It's been a pleasure.
I really enjoyed discussing with Christian. As we talked, we are happy to hand out a few digital copies of the book for you, dear listeners. So, send an email to email@example.com, the first few lucky winners will have a copy for themselves. Also, if you follow Eficode at social media, we are going to put out a discount code for the book. We'd like to thank Christian's publisher, Manning Publications for the generosity. Well, that's all for now. I'll talk to you later, but in the meanwhile, remember to make change easy and make an easy change.