 I'm Chris Chang, you can find me on GitHub at CRC Check, my Twitter handle is ccrrccrccr. You can also find me on IMDB, I'm Chris Chang too. I'm also looking for more Pinterest friends, I'm Cphanel on Pinterest. I currently work at Tabbed Out, but a lot of the knowledge that I'm going to be sharing is things I've learned from a lot of my time at the Texas Tribune too. You may have seen a lot of other Texas Tribune people around, oh I see something right there. So everyone always talks like this with dictionary definitions, Wikipedia says technical debt can be thought of as work that needs to be done before a particular job can be considered complete or proper, and then something Gene Kim says is left unchecked, technical debt will ensure that the only work that gets done is unplanned work. Another thing Gene Kim says about technical debt is that it's what you feel the next time you want to make a change. And for me personally, technical debt is every line of code. And just in case you're curious, I checked and Urban Dictionary does not have a definition for technical debt, but I also think it's important to just know about what technical debt feels like, and it's when you fix one bug, only to create another bug. And it's when you can't even deliver features anymore, because you don't know what repercussions your change will have. And it can get so bad where you can't even install security patches because you don't know what will break. And it's when you're using a rapid development framework and you can't rapidly develop. It's when you hear a lot of, it works for me around the office every time you're trying to replicate something. And the great thing about Django is it does let you build things very quickly, but it can also get you into this technical debt trap. And the main thing that I learned is that you don't want to let Django become the scapegoat for your team's inability to develop new features. And the last thing about technical debt is it's like when everything is on fire all the time, and this is your life. So like there's no magic trip tips for how to clean and technical debt. So anyways, here's some tips. I've kind of organized them from things you can do on your own to things that you can do amongst your team and finally things that require going outside your team and to the non-technical people in your organization. So first thing is Django testing. You should do it. There's three other sessions at DjangoCon about testing. Don't let your dreams be dreams. Yesterday you said you would write tests today, so just do it. Make your dreams come true. Just do it. So when should you write a test? Because a lot of times a lot of people will just, there's a lot of, I find that there's a lot of reluctance to writing tests in the first place. So maybe if you can ease people in, that would help a lot. And something that I always thought was if you're making an assumption, that's a good sign that you should be writing a test to check that assumption. And I can see justifying not writing a lot of tests up front, like doing TDD. A lot of people might consider that premature optimization to be writing a lot of tests up front. But if you're doing that you should at least be doing regression testing as Wise Man once said, fool me once, shame on you, fool me, you can't get fooled again. And you will get, you will sleep better when you have good test coverage. That's a fact. So I want to give an example about testing your assumption. Let's say I had a function where I just wanted to turn in a zip plus nine or a zip, a U.S. postal zip, I just wanted the first five. So that's pretty easy. I mean, it's a string, I'll just take the first five. But if you look at these examples I've set up, and I've written this as in the form of a doc test, which I personally don't use, but it fits nice on a slide. You start to see at the bottom my assumption that taking the first five digits falls apart. And if you have tests then you'll be able to catch that. And if you don't have a single test, here's one that someone posted recently, and that's if you just use the client to load your homepage, I don't have the facts to back it up, but you'll get 80% test coverage just from this one test. And the thing that I really like is if you like a feature, you better write a test for it. So coverage is another great tool for just helping you discover where you should be where you're missing tests. And a lot of organizations will strive to get 100% coverage, but it's more important that your tests are quality. If you're writing a test to get, for example, just checking the Unicode method of a model, may or may not actually be useful, you'll have to find that on your own. So some signs that your tests are crappy. One is like, can other people read and extend your tests? Will they take your tests and use it as a template for their own tests and be happy with it? Are the tests getting maintained whenever the code is getting maintained? Or are people getting frustrated because they have to also maintain the tests? And the tests describe the problem in a way where you could throw away the code and people could use the test to rebuild the code. And it's not only the tests, but the same could be said about your documentation and maybe even your readme. Could you throw away all your code and could someone use the readme to reconstruct your entire application? Another thing that's a big source of technical debt is bit rot, also known as software entropy. And I think that if you're not actively touching a piece of code, it's getting worse just lying around. It's also a sign for me, it's a smell as an organization if you don't have the manpower to actively maintain all of your code. And if you're interested in this kind of learning more about this, there's a series of management books called the Improvement Cota, also known as the Toyota Cota. Every time I researched that though, all I got was a bunch of marketing seminars that were trying to sell me something. But maybe you'll have better luck. So if you imagine the code of your organization as a globe, are there areas where there's like hereby dragons where people just don't know what that code is and they don't want to touch it and no one knows what it is? That's why it's important that everyone at the organization knows as much as the code as possible. And one tool that will help developers be a lot more productive in this is continuous integration. One quote that I made up based off of this other quote is, any task you do at least once a month should take less time than it takes to get a coffee. There's a post, it's been there the whole time. There's this guide here that is a really great intro to a lot of CI tools. Some that I particularly like are Jenkins if you're doing stuff yourself. Travis CI is really great if you're doing open source stuff. The great thing about Travis CI is you can even do like post GIS testing. I didn't know you could do that till last year and I think it's great that a free tool will let you write tests that require postures. Tox is another great tool. It'll help you run your test suite against different versions of Python and Django just to know will the next version of Django break my tool or not? Here's an XKCD I really like that just basically tells you whether or not it's worth to automate a task. And here's the opposite XKCD saying that you should never automate a task, I guess. So code quality is also a really basic thing. There's a difference between a senior developer and a junior developer. Practice makes perfect. Always iterate on your code. A great thing to help with this is just working on open source projects. Keep your code simple. Code that requires less context to read is more understandable. So by keeping things simple, I don't mean keep your feature set as dumb down as possible. You can do fancy stuff in your code as long as it's a pattern that's reused across your code base and that the people you're working with are also familiar with it. For example, if you're in a Django project and you're using pandas to do some filtering, that may not be simple, but if everyone on your team is also a data scientist, that may be the most obvious tool for everyone on the team. Avoid special snowflake patterns. I always choose readability over performance. Let the tools catch up to coding style, not the other way around. It's not like Python is being directly executed by your computer anyways. And the great thing is Python has something built in called import this, and if you never run it, this is what it looks like. Except I was just wondering if anyone else constantly misspells import like I do. I cut it off, but import this will just give you the Zen of Python by Tim Peters, and I actually had it printed out and put it up on my desk. And if you're looking at a piece of code and you're wondering what you should be doing with it, it's good to have this in the back of your mind too. Here's an example of two ways to do the same thing. Let's say I've got an iterable of objects and I want to get the Instagram attribute out. I really love functional style, so I personally love using map, but every time I do it in code review, my teammates say I should just use a list comprehension. So I guess now I do a list comprehension. But I guess just look at this like right now and just see which one looks more readable to you. And there's so much discussion around code quality. Here are some of the keywords you can look up. There's whether or not comments actually help or hurt code readability. There's something I really like called cyclomatic complexity. The great thing is that you can quantify readability, and if you throw this into something like a Jenkins job, you can actually track how readable your code is over time. There's the whole Pepe discussion, and PyFlex is kind of an extension of that. So I mentioned code review. Code review is probably the same most practical way that you as a team can get better. A lot of people say that people take too much time to review, and people just want to deliver. And some people might say, I'm the boss, or I'm the big boss. I don't need code review. Or we need to push this Hophix out ASAP. But we all know that coding under pressure is the best. And it's always perfect. Let's say you're a team of one person. Can you still do code review? You can, I guess. What I do a lot is I'll sleep on a change. Just opening a pull request puts all the code in front of you. It gives you a new view of everything. You'll discover patterns that were, oh, why did I name it one way up here? And change the name down here. That's things you can't see on a commit level. But you'll be able to catch that on a pull request. And if you just want to practice, you can practice with others on an open source project. And this quote I really like, it's as iron sharpen, it's iron. So does one person sharpens another. And that really applies, I think, to code reviews. And to do a code review, you do a pull request, usually. And the general rule about pull requests is, a lot of people would say the best practice for pull request is one pull request is only one change. And I am saying, if you want to reduce technical debt, you should bend this rule. There's something I learned called the Boy Scout rule. And I thought it was do a good turn daily. And I wasn't sure what that had to do with pull requests. And then I thought it was be prepared. And I wasn't sure how that worked either. But it turns out that the Boy Scout rule is based off of the quote from Robert Baden-Powell, leave the world a little better than you found it. And the idea is, if you're in a pull request and you're going in and you see typos, just go ahead and fix them. If you see little mistakes here and there, go ahead and fix them while you're in the code while they're right there in front of you. And the cutoff for this, though, I would say is, if you start having to mess with tests while you're doing this, that's a sign that this little change you're making is going to be, is going to actually hurt the readability of the diff enough of the pull requests where you should leave it alone, open a ticket, revisit it later. And code review is also a lot more than just reviewing code. When you're reviewing code, you should also be making sure that what the code is doing is repeatable. I guess, for example, that is I wrote this utility last week and I was trying to run it and it wasn't working. So I went back to the pull request that added it and discovered that I was using the utility in the complete wrong way. But I had it documented in the pull request with the right way to use it was. And also along with the code is documentation. For example, if you're adding a new feature to your CMS, there's users for that CMS. You have to make sure that they know that this change is coming and they know you have to educate them about the changes that are upcoming. I think I just said that. And then there's also just, is this code maintainable? Does the feature you're adding justify the technical debt that you're adding? For example, you may decide that to get this in the template, you use a template tag. But then during code review, you may decide, wait a second, you can just use this jQuery one-liner to do the same thing. And that's an example of doing like two things, one with significantly more technical debt than the other. And if you're really interested in code review, I think Raymond Heddinger's Beyond Pepe Talk is really great. It is the most hyped talk that I've ever heard, I think. And I think it justifies it. And going back to how, if something is maintainable or not, there's something that I liked doing on my pull request at the Texas Tribune. And that's, I would add a technical debt note. And the basis of that is on fiscal bills, they would have something called a fiscal note. And I'll read with the legislative budget board of Texas says about that. And just substitute in your head the technical pull request version. A fiscal note is a written estimate of the cost savings, revenue gain, or revenue loss that may result from an implementation of requirements in a bill or joint resolution. It serves as a tool to help legislators better understand how a bill might impact the state budget as a whole, individual agencies, and in some instances local governments. So you can see the idea is that when you're building code, you have to make sure that you can deliver it over the long term. And probably the best way to actually really accelerate the code review process is paraprogramming. It's like a code review, but instead of having to wait for someone to comment, then you're like right next to them and it only takes a few seconds. My preferred tools for doing this are TeamX and Vim. But you can also just stand next to each other. We're not stand or sit. And just be on the same, you can even be on the same machine. In this case, the human is the driver and the cat is the navigator. And there's also a variation of this that I've learned about. I've never tried it, but I'm curious about it. It's called PeanPonPairGramming. And so where one person writes a test, then the other person implements the test. And then they swap it back and forth. And the difference, one difference is that with PeanPonPairGramming, it's actually an async process. You don't actually have to be there next to each other. So you can do it, I mean, you won't have it as tight as a feedback loop as actually being next to each other. But you can get close to true pair programming without that. And it's also very important that you just get along with your teammates. When you're hiring, you should make sure that you only hire people that you get along with. I know back at the Tribune, we assumed that once people came to us, they were technically proficient. And at that point, we would just hire them based off of whether or not we would get along with them highly. And I would say that the worst coworker ever is probably past you. So now going outside the organization, I mentioned this a few times already, but any action you can take to get a faster feedback loop over every action and every change is something that you're going to try and do, want to do. You should learn how to say no. You should learn how to embrace failure as an organization. What I like to do is try to make myself obsolete. I think you should always work like you're training your replacement. And the idea behind that is that if you're automating away all the routine stuff you have to do, it frees you up to do the more creative things. And can you go on vacation or a conference without bringing your laptop? And as an organization, you should admit that technical debt is a problem. That way you can start treating technical debt as a ticket to be worked on instead of something that just swept under the carpet. If you treat paying off technical debt as a feature, then you can actually act upon it. One thing that'll help is actually writing out your technical debt items as actionable small tasks. Something like upgrade Django to version, change this old view to this new pattern that we started using. And something I like doing is I really like just writing a list of technical debt. And I use fire for things that are going to blow up. I use poop for things that might be publicly embarrassing. And then I use this table flip guy and I change the size of the table based off of how frustrating it will be to code the solution. And so there's some things about agile that I think even with you're using agile or not, there's things you can take away from it. The main thing is it's not just about sprints and tasks, but it's also what I really like about agile is you're always improving the process that you work. And I think a lot of agile is actually just common sense wrapped in marketing jargon. So Django is great. It lets you write things with less technical debt, I think. The testing framework of Django is the best I've ever seen anywhere else. Every time I use another framework, I wish I was using Django just for the testing. It has batteries included. There's less boilerplate to do a lot of the same things. And it's got a lot, it's got a great community. If there's a feature you want, someone else has probably already worked on it. But I do have some tips for working with Django. One is don't customize the Django admin. Think of the admin as a developer interface, not a CMS. If it seems like it's a lot of technical debt to write your own CMS, it's based off of my experience, it's actually not. Because when you're writing an extension to the Django admin, what you're doing is you're tightly coupling your admin to something that keeps you from upgrading Django in the future. Because guess what, the Django admin changes without every version too. And a side benefit is if you write your own CMS, you'll often end up with a better user experience for your customers. And if you want to use a custom admin element, guess what? There's hundreds of them. This is just a screenshot of the Django packages page for admin extensions. The other thing you can do is clean as you cook. When you're in your code, just delete it. That's what source control is for. If you think that there's a feature that you might actually want, don't comment it out with a block comment. Wrap it in an if statement. Because if it's something that you actually may want to turn on and off, you should treat it like a feature switch and not like a code block. You could even extend that and use something like Django Waffle or an AP testing framework to actually turn it into a feature switch. And the main thing is if you find something, if you see something, say something. If you see something terrible, make a ticket for it so it's not forgotten. Another thing with Django is naming things is also really important. There's actually a talk about this on Wednesday. Are the names you're picking greppable? For example, if you're using S for a variable name and you want to find and replace all instances of S, you're going to have a bad time. Use unique app and model names. And the main reason for that is the Django will let you have your own namespaces and build your own apps and model names, but there's enough places where they all come together where it's worth it just to try and make everything unique. And the other thing is make your variables in your test read as if they're English. And for example, here's a contrived testing example. Let's say I have an article and I want a manager that only gives me the active ones. So I've got all these required fields. So when I make an article, I have to add all these fields. But here's an example using factory boy where now my test setup only has what's relevant to the test. If I'm only testing active articles, here I've explicitly said, give me an article that's active. And then as opposed to like assert A is in this query set, now it's assert this article is in this query set, which reads more like English. And the thing about unique names is I really love shell plus, which is part of Django extensions. A great thing it does is from one line, you can start missing with objects immediately. And if you don't have unique names using shell plus, you lose that ability of shell plus unless you start adding more technical debt in the form of shims around shell plus. And another tip I would have say is avoid the Django test client. Don't use it for unit tests, but do use it for integration tests. Because when you use the test client, you're not only hitting your view, you're also hitting the URL router, the request middleware, all the parts of the review, the response middleware, the context processor. And who knows what else? I would say avoid model inheritance. There's not to plug Django extensions again, but Django extensions does come with some examples of what an abstract base class or a mix-in should do. There's a timestamp, date field, there's a title, slug field, things like that that are very routine. You can definitely do that as a mix-in, but you shouldn't read the feature that Django has model mix-ins and then jump to try and use it everywhere. For one thing, it'll make it very hard to trace what field comes from where and where you're jumping around. You'll just get lost very quickly. Another thing is this goes back to kind of touching everything. Something I like to do is every time I have a pull request, I just like checking to see what's at a date, and I like bumping any requirement that I can. Of course, this only works when you have test coverage. And the other thing you can do is you can trade, you can purposely get rid of technical debt and purposely give yourself technical debt for depending on what you're optimizing for. Something that I think freed up the work at the Texas Tribune a lot is something I call a Tuck-up Bell feature. And there were a lot of sections of the site that were frozen, and there was a lot of content. But the problem is even though this was old content, whenever we added new content, it would change all the old content as a side effect. So the idea was, is there a way to freeze this content in a way so that we can continue developing without having to worry about what changed? And you should ask me or anyone else about how that works. And the great thing is after we did that, we deleted so many templates, so many CSS files. We could delete so many models after that. It was amazing. And when you're developing a feature, you should decide upfront what you're optimizing for. Is it more important that you deliver the MPVP as quickly as possible, or is it more important that this be something that that it can sustain for a while? Like if you're a startup, getting the first to market is a real thing that is probably more important than building something maintainable. I'm surprised how many people haven't really seen this. This works for contractors, but it also works for code. You should decide, good fast or cheap, pick two. You can't pick three. You can also pick one, but what are you doing? And so I like to write crappy code sometimes. My general rule is if everything can fit in one screen of code, I can make it as crappy as I want. In fact, if this is throwaway code, I introduce errors on purpose just to make it look, just to make it obvious. Because there's nothing worse than finding a piece of code that was hastily written, but also has unit tests and documentation and then realizing that the author had no idea what they were doing and was just making stuff up. So in preparation for this, there's a lot of material about this that I found I really enjoyed. These are actually videos, not reading. I've got links to these, and if you just Google these words, they'll also take you to the right place. Here's some reading lists that are, here's actual reading. I actually read a real book in preparation for this called The Phoenix Project. So you'll have to find that in a bookstore. Look for this book when you're there. And if you can't find that book, look for this book. So there's a lot of other similar tangential discussions about that. Each one of these could be their own talk and are talks too. There's like microservices versus monoliths. There's the single responsibility principle. One that I really like is knowing about the not invented here principle or syndrome. Being aware of feature creep, you don't want your app to become this huge giant mutant. And I put insert product development buzzword here. But really, if you're really caring about this stuff, you should look into, go outside, technical talks and just look at business strategy and product development. So this is my homework for you. These are all the important things. You should get frustrated while you're at work, but get frustrated in a constructive way. I also think you should be as lazy as possible. Automate away as many routine things as possible. Try to always get a faster feedback loop in any process. Always be touching all your code. Treat fixing and technical debt as a feature. Clean as you code. Foster a generally good coding culture at your office. Remember that everything must be repeatable and plan ahead for the whole life cycle of the product. And thanks. Keep making Django sausages. There's a link to view my talk. And for some reason, you can also run my talk as a Docker container. Okay, question. So when you're coding along and you're looking at this code and it's all building up and inevitably every programmer in the world looks at and goes, I'm going to have to change that later. I just know I'm going to have to change that later. But I need to get this out. How do you decide? Because inevitably some technical debt will build up because of schedule, because of lack of information at that moment. But you could in theory stop and take the time to do it the perfect way. And either way could be reasonable. So how do you make that call? I'd say it's called by by management almost. And what I like to do is I like to litter my code with with to do's. And I have like keywords I use. One thing that I really liked is with you can have a Jenkins job actually that just indexes all the to do's in your code. And you can kind of track that over time too. Thank you question. And just to note statements to begin, I have a comment or not a question. Do you have any experience with a wide variety of skill sets where some people are far ahead with newer technology or far ahead with more of the legacy things you're working with and keeping people on the same page when moving forward? Yeah, having as much overlap as possible is something you should strive to do. Pair programming is the best way to achieve that. But it's everyone's different. So that's not a real answer. You should try to do code review and pair programming. They can. And I kind of like that. But they can also turn into bike shedding sessions too, which is why you should keep the pool request as small as possible. Where does where does bit rot come from if the code itself isn't actually changing? I hadn't heard that. Well, I think the requirements is a good example. If you're if you can't if you freeze your requirements and you never upgraded them, your app will always work. But eventually you'll want to make a change and you'll discover that it's not backwards compatible. There's one of my links actually talks about the organizational debt. Is the way we look at tickle is wrong. One of the points he makes is that if code is left alone, it's fine as long as it works. And there's the truth in that, too. If you've got code that's drowned and works, I would say just if you have to change it, be prepared to throw the whole thing away, though. Hi, thanks for the talk. A really important topic, especially for large projects. I wanted to ask you about Django Waffle and AB testing framework. How do you keep something like that from kind of exploding all the branches in your code? And like, how do you manage all that complexity it creates? I don't have that much experience with Waffle, but in my own mental planning for that, I always thought if you can track what switches you're actually using and make it a task to go through and turn dead switches to static code. If you make that a feature that you have to code and you plan for, then I would say that's how I would deal with that. Hey, thanks for the awesome talk. It was great. I was wondering, do you have any tips for how to incentivize good code quality within a team? Because I find like the stick method is definitely much less effective than the carrot method of like getting developers to buy into this kind of stuff. Yeah, my thought about this is to use bots. Basically, if you have a GitHub as a new feature where you can't merge pull requests and let us meet criteria now, some people just may not listen. That's why also like the whole hiring thing is also part of it too. It's kind of terrible, but sometimes you just need to change your team. But also, people may have a hard time listening to a human, but if you have a bot that says the simple medical complexity of your code is too high, reduce it, or the code coverage is not there at it, I want to say that people will look more likely to listen to a bot than another human. And thank you very much.