Creating an Inclusive Django Community with Kenya Phelps
Published July 15, 2026
This video features Casey Kinsey at DjangoCon US 2014 in Portland, Oregon, USA.
By, Casey Kinsey
In an industry where “lean” has become the mantra and rapidly iterated products imply tight budgets and tighter deadlines, how do you effectively take ownership of someone’s hastily written Django code? In this talk, we’ll dive into a step-by-step process for dicing up legacy projects, short-sighted prototypes, and plain ol’ spaghetti code to turn them into codebases you’ll show off with pride.
Help us caption & translate this video!
Casey Kinsey explains that sloppy code often comes from deadline pressure, rapid prototyping that quietly becomes production software, and a lack of long-term ownership. They recommend first preserving the inherited code, reproducing its last known working state, and building broad integration tests plus focused tests for business-critical logic before refactoring. The cleanup process includes auditing and reducing dependencies, upgrading packages, restructuring monolithic or overly fragmented Django apps, removing unnecessary signals, context processors, and middleware, documenting code as it is understood, and addressing hard-coded URLs, broad exception handling, unstable VCS dependencies, misleading names, and premature configuration.
Summarised automatically from the transcript.
Automatically transcribed, so expect mistakes in names and technical terms.
Speaker 1: Yeah, we're going to talk about sloppy code today. My name is Casey Kinsey. There are a bunch of links there where you can stop me on the internet. And I'm a developer consultant from Fayetteville, Arkansas. By day, I'm the president and chief actor of my currently one-man consulting shop in Lofty Labs. And by night, I am the co-founder of a project called HeapSort. uh which is a career website and job board for full stack web developers. These stacks are uh sorry these slides are available uh online at the Lofty Labs website. So you can go there right now. They're built in HTML and JavaScript. So you can use them to follow along. There will be some code samples and I think you'll be able to see them easier.
Speaker 1: And that's all contingent upon my uh $5 Digital ocean droplet not crashing when you all go to pull it up. Um so before we get started talking about actually dissecting the code, I want to spend a moment Talking about where sloppy code comes from. I think the obvious place is an experience. That's the first thing you would say. And it makes sense. We were all beginners at some point. But that's not what we want to talk about today. I'm not concerned about that. If you're new to Django and Python development, then and you're here in the crowd today, then you've done the best thing you can do, which is to come here, and you're going to learn a lot over the next couple of days. No, I'm more concerned about the sort of sloppiness
Speaker 1: that comes from things like an emphasis on development speed, pushing code out the door quickly, strict deadlines. an incentive to cut corners. And there's also something I've noticed in my time working as a consultant, as a contractor, and it's a problem that comes from it's it's a psychological disposition. the lack of long-term ownership of a project and what that does to the code that gets delivered. If you don't have long-term ownership you lose incentive to make sure that the project is stable for the next person that has to inherit it. And this is a little bit off topic. But I I I wanted to point out something. As I was looking at this, there's there's sort of a sweet spot between these two points, number two and number three.
Speaker 1: Lack of long-term ownership and an emphasis on development speed. That occurs within a concept that's somewhat fashionable in our industry right now. And it stems from this idea of rapid prototyping. Which is really popular with modern frameworks because one of the benefits of modern frameworks is that on the long term we can build something that's very complex and sophisticated, but in the short term we can build something that's relatively complex and sophisticated without a whole lot of time being spent. But I'm gonna say something controversial because I think rapid prototyping is a lie. And I don't mean that the concept itself is wrong and that it it's something that was lied to us about.
Speaker 1: I think it's something that we ourselves, uh it's a lie that we tell ourselves. And it has everything to do with this word prototyping, what we call prototyping. Because in the rest of the engineering world, a prototype is strictly a proof of concept. And those prototypes never see production. This is a prototype Tesla Model S. Okay, obviously this car is not driving around the highway somewhere. And it never showed up on a showroom floor looking like that. It's an inherent part of the engineering process when you build something physical that at a certain point the prototype gets thrown away. And you take what you've learned and you build what you use in production.
Speaker 1: When engineers build a new type of suspension bridge, it's built on a very small scale. Obviously no car ever drives across a model the size of this table. But they take what they learn and they throw that out. Because you can't bend the metal and create something new from it. It's inherent in the process. And that's not something that happens in software engineering. And I would be wrong to say that it's not a benefit to us as software engineers that we don't have to throw things out to go to production. We can bend the metal, take what we have, make it better, and we don't have to start from scratch every time. And while that's a benefit It also can be used very much to our disadvantage because it's not inherent in the process that a prototype stops being a prototype
Speaker 1: Because in software engineering, prototypes go into product production all the time. Because we use the word prototype always to our advantage. It's not fast or it's not very efficient. Well that's okay. It's a prototype. Oh all right. But then one day the client comes and they say we need to ship it right now. Or that deadline comes up and then suddenly it's not a prototype anymore. We slapped the word beta next to it, which beta is just another word for prototype that went into production, right? So I I want this to be on the back of your mind as we talk about uh the rest of the the topic today. I want you to think about it because it's important to Understand
Speaker 1: this isn't the only way that sloppy software gets produced, but it's one of the big ways that I've seen in my career And so understanding where sloppy code comes from helps us to fix it because we understand the mindset of the person who built it and also helps us not to be the next person that bumps the version number and pushes the prototype out into production. Okay, to work on a sloppy code base, you're gonna need some tools. And you're gonna need to be familiar with those tools, the first of which is a good editor or integrated development environment. And there are tons of options here, but what we're really looking for is something that offers code navigation tools. So being able to quickly move around a project,
Speaker 1: that's really important when you get a new project you've never seen before. Being able to highlight a symbol and then immediately go to where it's defined. Okay, that'll help you learn where everything lives And then beyond that, you have code introspection. And this is where your editor has a Pythonic understanding of the code you're working on uh and you get things like code completion or being able to work with a model and call one of its methods and and see what the signature looks like, know all the keyword arguments uh that that method accepts. That's a huge plus working with unfamiliar code. And then lastly, refactoring tools, which is anything stronger than just a brute force find and replace across a bunch of files. being able to find methods that you want to change and introduce a variable to them and tools that let you find everywhere that it's used across the project.
Speaker 1: So you're not just blindly changing everything or manually doing it one by one. Okay, so those are pretty broad. There are a lot of tools that that give you all of that and more. I personally use PyCharm. I think it's great. There's also Pydev, which is uh built on Eclipse. Um Emacs, who uses Emacs? Yeah, people get enthusiastic about Emacs And I understand. I don't personally use it, but like it can literally do anything. You can e-file your taxes with Emacs. So if you can stomach the learning curve, Emacs is another great one. You're gonna need to be familiar with a debugger and know the Django debug toolbar doesn't count.
Speaker 1: That's not the type of debugger I'm talking about. I'm talking about a full debugger. Um if you're not familiar with one. Then you need to find one and and just get familiar with the basic operation of it. I'm not an aficionado of debuggers. I use the one that ships integrated with my IDE. There's PDB, IPDB P U D B, pretty much any combination of the letters P, D, B, optionally a fourth character is a legitimate Python debugger. And then for bonus points, like I said, have that integrated with your IDE. Rather than just running it, being able to set breakpoints as you're editing. That's going to be really crucial. You're going to want to know how to use coverage. py , which is a tool for checking test
Speaker 1: coverage. You run your test suite and it tells you how much of your code base was exercised. It's super easy to use. You just run your test command through coverage. This is sort of my go-to starting point for coverage configuration, the coverage RC file. It uh tells coverage to Check everything in the current directory and below and omit things like migrations, whiskey files, settings files, things that aren't normally exercised by your test suite. That uh would arbitrarily bring down your coverage report. And then you generate a report. It tells you all your files, how many statements were run, how many were missed, gives you a percentage. Very easy. And then um there's Pylint and PEP8. And these are both tools to help you in the actual writing part of writing code.
Speaker 1: So Pylint helps you clean up poorly structured code. It's really useful during refactors. PEP 8, if you're not familiar with PEP 8, that's the Python enhancement proposal number 8, which sets the de facto standard for Python's style guide. Stylistically how Python code is written. And then PEP8 lowercase is the Python package that looks at your code and tells you when you break those rules. PEP8 will keep your code legible. It has one major caveat, and that is it'll make you irreversibly anal. about Python code and you will become intolerant of non-PEP8 code. And and it's it's worth it because you end up writing good looking code and and uh when you have that standard in place.
Speaker 1: You know that the next guy will will be able to read what you've written. And then again, having these integrated with your editor or your IDE uh really helps. It's better than just running it against your code base you get the response, the feedback from these tools immediately as you're writing code, which is very helpful. Okay. You've just gotten your hands on a sloppy code base. You've been given the keys to version control. And you're gonna start looking through it, and your immediate reaction is going to be to start tearing it apart. Right, you're gonna start digging through the code, seeing what you've gotten yourself into. You're gonna see things you hate and you wanna change it. Don't touch anything. At this point, everything in the project is hot lava.
Speaker 1: And if you attended DjangoCon last year, um I gave a talk about hot lava. Well, hot lava made an appearance in it. When I was a kid, I played a game, the floor was hot lava. If you touched it, you will die. So don't touch anything yet. First you're gonna have to do some prep work. First thing, make a copy of the code. Okay? Use your version control of choice. Check out a copy of the branch as it stands when you get it. Move it into a separate branch. This makes it really easy to use tools like diff. And as you're making edits to code, you inevitably delete something that you needed back or you need to see the way it originally worked, and you can do that quickly. Even better when it's all said and done, you can get a list of how many lines of code you changed and use it as your badge of honor.
Speaker 1: And now you want to get the project started up. But again, we're not touching anything. Our goal is to get it running in its last known working configuration. Working. So you create a virtual environment. Don't get attached to it. They're disposable. If the project shipped with a dependency list, Use it, that'll save you some time. Otherwise, you're gonna have to manually figure out everything that you need. If the project shipped with tests, count your blessings. And try to get them running. But keeping in mind that if the project really is in a state of disarray, there's no guarantee that the test suite that you were delivered was actually passing in the last known working. configuration. So if it shows you configuration problems, that'll be helpful.
Speaker 1: But if you start seeing logical issues, obviously failing tests. uh that are caused by the code itself failing, then don't spend too much time with it. You're either gonna have to fix those tests or throw them out later. And then be sure to freeze any changes you make. You can't remove any dependencies at this point, but if you add some or if there wasn't an original dependencies list at this point, freeze that all so that you have it for later. Now you're going to need test coverage before you get started. But we're not going to go too crazy here. We're looking for integration test coverage for high-level Django concepts And we're looking for integration or unit test coverage, whichever is most appropriate, for low-level and business critical processes.
Speaker 1: Okay? And when I say integration test, for our purposes, I'm talking about an end-to-end test of the Python and Django code. Our goal is coverage here. We want to cover as many lines of code in the project as possible because we're looking for landmines. We're looking for things that blow up unexpectedly. We want to see that when we make a refactor in this component, it didn't break something in that component over there. We're essentially automating the process of clicking on every page on the site and making sure you didn't get an error message. Automated smoke tests. And this is the type of test that I'm talking about, an example of how it would be written. This is from Heapsort, and in this case we're testing the homepage as an anonymous user. We're just
Speaker 1: Hitting the page, checking the status code 200, and then I like to use this assertion for this kind of test to make sure that the appropriate template was loaded. Sometimes views can construct their own responses. Sometimes they can return the output of another view. So we want to make sure that the user is kind of seeing what he's supposed to see. And notice that we're not checking any data on this page. We're not checking to make sure that the appropriate things were rendered there We're just making sure they ended up in the right place and an error wasn't thrown. And this is for an anonymous user. So when you write these types of tests, they're very quick and easy to write because you don't have to do much, but you do have to catch every user path. So if a user is logged in, you would want a separate test for that. On heap sort we have two types of users. We have our candidates and we have our employers.
Speaker 1: So there's three tests on heapsort for everything like that If the uh the view implements a form, you want to test git and post. So all of the user paths for every view, but very simple tests. Now for unit tests, our goal is not full unit test coverage. That's not something that you necessarily have to strive for at this phase. Because as you go through rewriting all the sloppy code, you're going to negate the work that you do writing unit tests. Now, you can take testing as far as you want here. And if you believe in test-driven development, then then go for it. And you will want to do this. But in my opinion, as I'm not a test-driven uh developer, I know that I'm going to write unit tests and then have to throw them away as I rewrite the code.
Speaker 1: Instead, we want to focus unit testing effort on core components where accuracy is critical. This goes back to the business critical processes. And this is an example of that type of unit test. We're looking at another test from HeapSort. We sell job ads on the site. And after 30 days, those ads expire. That's business critical to us. That's what enforces our value add. So we want this type of test to make sure that it that an ad no longer shows up on the website after 30 days. And that's enforced with a manager. We have a manager here, the active job manager. Now this isn't the purest uh example of a unit test. Like we're still invoking database machinery and all of that, but that's okay.
Speaker 1: We're not using the test client. We're not doing this from the page level down. We're specifically testing a real figure 30 days and making sure it reacts properly. That's the type of unit test you should strive for when you first get your hands on a project like this So, how much is enough? Integration test coverage for all Django views and user paths. Management commands are really easy to test. You just import call command and call it. Potentially third-party APIs if you can do that with the test client. We want unit test coverage for business critical processes and other logic. It says non-view, but really non-testable with the Django test client. So that's billing processes. If you're charging people money, you want to make sure that not only do they get charged, but they get charged the correct amount and only once
Speaker 1: Transactional communication, if your project sends out emails or otherwise notifies users, that's business critical. Asynchronous tasks, if you have an asynchronous task defined, more than likely that's business critical. And it's not something you can catch uh without writing unit tests. And we're going to shoot for 95 % coverage. I think that's the ideal scenario. I mean, more is better, but But that's the point that I would really feel comfortable moving on from. Coverage of 90% is actually pretty easy in most projects, especially when you write really broad integration tests like the examples that I gave. Even a medium-sized project, one or two developers, can get 90% test coverage in a couple days at most writing that kind of broad tests. So we're going to shoot for 95%.
Speaker 1: And we want no individual files uncovered. So if you have one file out there that's 50% coverage, that's not good enough. We want that level of coverage for all of the files. It's not as hard as it sounds. And then and only then is it time to start hacking on the project. And you're going to be really happy that you took the time to put those tests together. There's no telling what you're gonna find when you first get your hands on this type of project. And so this is not an all-inclusive list, obviously, but what follows is sort of my greatest hits, if you will of design patterns or any patterns that I've encountered working as a consultant. The first type is, I call it the patchwork quilt of dependencies pattern. And this is a real diff
Speaker 1: from a project I was working on just a couple weeks ago. I removed 133 external dependencies before I even got started. I had never seen anything like it. The problem with third-party dependencies is that all of them introduce complexities. Whether or not it's good to use one is just a matter of determining if the ends justify the means. Um each dependency is going to be a potential obstacle to upgrades. Okay, imagine if I decided I wanted to upgrade from Django 15 to 1. 6 on that project and I had to test 133 external dependencies. to make sure they were all Django 1. 6 compatible. And when you have a project like this, the project ends up being a lot of configuration that just makes all of these different things work together.
Speaker 1: Sort of like a system of plugins and that's it And these sort of low-level Django extensions often conflict with each other. They make debugging a nightmare. You go to look at some process, something simple seems to be going wrong, and it turns out there's four or five layers of abstraction between where you thought the data was coming from. and where you want it to be, and that's a pain. And then lastly, uh third-party modules installed from BCS, GitHub, things like that, uh make no stability or availability guarantees. So if you're just pulling someone, especially if you're just pulling the master branch of some repository out there, code can go into that any day. And it can disappear tomorrow. Okay? Excuse me. So you want to trim the fat.
Speaker 1: Anytime you get a project, you want to audit the dependencies. Remember that virtual environment that we created in the beginning? Good. Throw that shit away. Because you're gonna start over. You're gonna install the absolute bare minimum of requirements that you know you need. South, you know, obviously Django. Um core APIs. And then you're going to go through the requirements list you either updated or created, and you're going to audit each one one by one, researching them to know exactly what they do. Why they're used and how it works and then that's gonna put it into one of three categories. It's either totally unnecessary or not used. So 133 dependencies I removed 80% of them were installed, not even integrated. So those were easy to get rid of, weighed off my shoulders.
Speaker 1: They're gonna be potentially unnecessary. You feel like that's something you could get rid of. Um but you know you have to rewrite some of the code in order to achieve that. And so you make a judgment call at that point. If it's low enough effort, you may do it right then. Or maybe it's a part of um some larger refactoring task later. You want to get rid of that dependency. You're going to rewrite the module that uses it anyway, so you're going to come back to it. But make a note of it. And then lastly you'll find other things that turned out to be necessary and you leave those in place. Then you run your tests. So the tests help you to make sure you've gotten all of the dependencies that you absolutely need installed and that anything you removed wasn't actually necessary. If you miss something, the test lets you know. You go back to the previous step and you audit that dependency.
Speaker 1: Same rules. And you do this until uh all of your tests are passing. And now is the best time to actually Upgrade your packages. So one of the things I said was a complexity introduced by dependencies. They block upgradeability. At this point, your project is at its all-time low of third-party dependencies, the least amount of compatibility issues, it's a good time to try upgrading Django or any other package as far as you want to go. Upgrade, test, repeat. The next pattern, the monolithic app of death. Monoliths are an organizational nightmare. We're talking about one giant Django application that contains all of the functionality in your project. And the problem with that is you can't succinctly evaluate the functionality of any one component
Speaker 1: because you have to dig through the functionality of every other component that sits there alongside it. And for the same reason, nothing's portable. You can't pull any of these things out into their own module. You can't use them in another project if you wanted to open source them. And it's full of import star. Yeah, I heard uh someone someone cringed out there, and I don't blame you because I have no idea what I just imported. And when you have everything in one giant application, well it kind of makes sense that you would do it. Because every view is in one file, and every form that gets used by a view needs to be imported into that file, so you do it. But it's really hard to take this apart. We're talking about good old spaghetti code. I have no idea what depends on what.
Speaker 1: So to fix this, you're going to create a sane app structure, obviously, and one of the other really big benefits to building the test suite up front is that at this point you have a really good cross-section of how everything in the project works. You forced yourself to do it. And so you probably, if you have a monolith on your hands, have already determined, you've already been thinking about how you want to restructure this. So you just implement. You're going to get rid of those import stars. Pilent's really helpful here. So you just remove them. Pilent reports back to you every symbol that's being called in the file that hasn't been imported. And then you turn those into static lists of imports. So now you know where everything's being imported. And then you migrate models to their new applications. I've found that the best way to do it is to move the models and everything else sort of follows.
Speaker 1: You move the model, every form that depended on that model now can't find it, or you know, you change the import, you remove the import of the model. Pilant tells you which forms we're using that model. You move the forms. Every view that depended on the form is now reporting that it can't find its form. You move the views, and then at any point in this process If you think you've lost something or you want to make sure you didn't miss something that you need to move, you run the test. Now migrating models across apps happens a lot in these sort of patterns. It's kind of a pain If you're pre-production, you don't have any data, any real live data out there in the world that has to migrate, just squash the migrations, start over, it's worth it. Otherwise, you need to migrate the data across.
Speaker 1: If you're using South, go here. You know, Stack Overflow is usually something that I find to be like reactive. I go there after I have a problem. This is like one thing that I keep bookmarked at all times because there are great answers here and awesome discussions, techniques for using South to move models across easily. preserving your relationships, preserving things like content types, because those are things we don't think about that have to change in order to move from one app to the next. The labels all change. If you're not using South, then you Django Migrations. I haven't had a lot of opportunities to upgrade projects to the latest version of Django using new migrations. It seems like it's a little bit of a challenge from what I researched. It looks like you're kind of stuck doing things the old-fashioned way. Creating copies of the models and manually writing migrations and move it over
Speaker 1: unless you are comfortable writing raw SQL in your migration files. So it's kind of a pain, but it has to be done. Now we have the every model is an app pattern, which is like the exact opposite of the monolith. It causes the same problems though. It's another organizational nightmare. Again, you can't succinctly evaluate the functionality of any one component because all of its functionality is distributed across the entire code base. Again, nothing is portable, and you have a different type of import woe. The cyclical import. Anyone ever had to deal with uh cyclical imports? It's like one of the worst problems, I think. to have to solve. Um and it makes sense
Speaker 1: because if every model is in a walled garden, um at some point they're gonna have to interact with each other. At some point this app needs to import that app and the other way around, inevitably this happens. You're gonna do the same things here that you would do with a monolith. So same challenges. You've got to rebuild an organized structure and move move the models across. Everything else tends to follow. Good news is less work probably because some of your apps will host the others, so you don't necessarily have to move every model. I guess my point there is that between these two design patterns, this is the one you'd rather get. And then um we're gonna move to the next one. Uh receivers everywhere. The Django
Speaker 1: signal dispatch system is so powerful and useful. Because signals allow us to decouple models which initiate side effects on other models, especially in third-party code. The easy example here is user profiles, right? For a lot of us uh that have come from Django even before Django 1. 5, I would say. Probably the first signal we ever wrote was one that created a user profile when a contrib auth user was created. And signals are great for maintaining data integrity. When data changes here, I need data to change over here. Otherwise, I lose data integrity There's a reason why most of the signals that ship with Django are built into the ORM and model machinery, pre-save, post-save.
Speaker 1: But they're not always useful because receivers can hide important functionality from developers, okay? Especially you inheriting this project. There's probably stuff lurking around there in signals that you won't necessarily see. And they become unnecessary types of abstraction between two models in the same application. If two models are in the same application, in the same models. py file, what need is there to decouple them with signals? And they can implement overzealous business logic, logic that belongs in the views, but occurs at the database level when things change in the ORM. So you want to be on the lookout for signal receivers that interact between two database-related models.
Speaker 1: I'm talking about a database relationship, foreign key, mini-to-mini join table. Or signal receivers in which the instance simply operates on itself. Because in both of these cases, the logic really this, I mean, here, pre-save and post-save. Those those are your receivers. You can do this in the model definition itself because it's a matter of data integrity. It belongs here. There's no reason why the model can't change a property of itself. It has access to all of its relationships. So you can do that here and there's no reason, there's no way for it to accidentally be hidden from me. When I look at this model, I know what happens when it gets saved. And then what you really want to watch out for is that business logic. And I'm not talking about database transactions, I'm talking about user transaction.
Speaker 1: User clicks a button, some side effect happens. That doesn't belong in signals. I've seen this happen in five or six different projects, okay? This is a signal receiver that sends an email. And there's a special type of terror that only happens when you're playing around in a Python shell and realize that you just send out emails to every customer in your database So don't do that. Don't write email this is business logic, right? We we don't want um we don't want to build a system where like you can't noodle around in the ORM without fear of some side effect happening. This is like Unless you are in the business of building a product and your value add is
Speaker 1: we will send you an email every time a database row gets updated, don't do this. So look for that. When you get a sloppy project, the point is to go track down all of the receivers. Even if they don't need to be rewritten, you want to find them all because logic can be moved away from the model that initiates it Right? These receivers can be registered anywhere. So go find them all and make sure you're aware of everything that happens there. And for things like this, kill it with fire. I couldn't think of a better name for this pattern. Um because they are cool. They're so powerful, there's so much you can do. And there's something kind of special that it's a feeling you get when you're like I've I've designed a system um
Speaker 1: that hasn't it's sophisticated enough to warrant using middleware to implement something. That's kind of a cool feeling. And it reminds me of when I discovered list comprehensions in Python. Okay? And for like three months I had just resolved to never write a for loop again in the traditional way. Yeah, one line. So cool. Because it was neat. And list comprehensions were powerful and I just wanted to use them all the time And I think that's part of the mentality of why context processors and middleware get so widely abused. Context processors can impair performance quickly. running logic, often database queries, every time a template gets rendered. And it's another place where where business logic hides.
Speaker 1: So you're looking around in this template and you don't know where this context is coming from, it's not in the view, it's hiding in a context processor somewhere. And you should evaluate your context processors when you get a sloppy project and see if The logic is better suited as a template tag. This is common when you have um common template elements, things like Data that gets presented in the header and footer. Maybe it gets included on the base template. And so you need it on every template that gets rendered, it seems like, so it gets put in the context processor. But now you can't build a template that doesn't have access to this data and you get 10 context processors. They can conflict with each other. This is a great time to use assignment tags. Perfect use case. Change it into an assignment tag, use it on the template in the same way, but now you can build other templates that don't necessarily have this logic
Speaker 1: and there's less there's less stuff piled up in your context processors There's also context processor logic that should just be refactored into views. So this is much more inefficient use of a context processor. I mean maybe it gets used in five different views And for whatever good reason, those five views live in different applications. Um But this is a perfect use case for class-based views. Create a base class or a mix-in. Um and even if you're not using class-based views There's no reason why you can't move it into sort of a neutral location, import it, and use it there. So audit context processors Middleware is potentially even more dangerous because it can run on request and response cycle and you can really modify things that make bizarre changes
Speaker 1: bizarre to you when you don't expect them, right? You're you can mess with the requests and responses. I don't have examples for how to fix it because it's so powerful and there's so much you can do with it. Whatever middleware you have will be esoteric to your project. But the question to ask yourself is Do I need this logic to execute on every single request and or response if it's implemented? Or maybe even better, do I need this logic to execute when Googlebot comes to the project? Okay? If you don't need it when Googlebot comes, there's a good chance you should engineer your way around it. And this isn't even a pattern. This is this is the absence of any sort of pattern.
Speaker 1: Good old undocumented code. I'm not talking about it didn't ship with a readme. I mean there's not a comment in the whole thing. Okay Like what does that do? I don't know if you can see it. If you're following with the uh the slides, you you should be able to. But I mean we have like these ambiguous variables. We have classification, classifications Level, class level, and string. We're accessing uh iterables like directly by uh explicit index. I have no idea what any of this does, and there's not one line of Commentary here to kind of help me out, okay? The only thing worse than code like this with no comments is code like this with just enough comments to piss you off.
Speaker 1: Returns a dictionary. Thank you for that.
Speaker 2: This is this is kinda complicated. I better drop a hint.
Speaker 1: Returns dick. And there's no easy way out of undocumented code. You can try and brute force your way through it and say like, you know, we're gonna assign to one developer, uh, he's obligated to write documentation for this whole project in a week or Or just try and knock it out all at once. I think that's impractical. You're going to be reading through all of this code anyway. So document as you go. And make it policy. Don't just say I'm gonna document as I go. The rule is if you edit the code. You document it. If you have to dissect some code, like the example we showed, and to understand how it works, you're gonna do that when you write tests probably, then you have to write comments letting others know what you find So for every module, function, method, class, you write a doc string, no exceptions, even if they're really stupid ones.
Speaker 1: Sometimes that happens. But if you get in the habit of always writing one, um That covers like most of your documentation effort. These are the workhorses. So at the very least, when I look at a class, when I look at a method or a function, I know what comes in and what I can expect to come out Like the previous example, long multi-step logical branches where data is being transformed in one way to get it to one format to transform it to another. Inevitably you'll have to read through that and figure out why it's doing it. And when you do, leave a comment Letting the next guy know or letting yourself know when you come back. You'll forget how it works. Break it apart with comments. If it's difficult to read, okay, or there's ambiguity, leave a comment
Speaker 1: And then I've got a list of other things to be on the lookout for. Not patterns in and of themselves, but stuff you can expect to find everywhere. Hard-coded URLs. You're going to want to try and remove those before you do any rewriting. You want to be able to move views around the project without all of your tests breaking. Broad try and accept blocks. Okay? Try accept pass. This is not PHP. We don't have a silence all error symbol for a reason. And even just try accept all exceptions. Even if it doesn't pass, if you catch every type of exception instead of a specific one, at some point you're going to silence a more helpful error in order to raise a less helpful one. So be very specific with tri-exap blocks.
Speaker 1: We talked about VCS requirements earlier. Fork those. Whoever owns the project should have a fork of that. So if you're the long-term owner, fork it in your GitHub repository. If the client is the long-term owner, make them create a GitHub organization Okay? They can have their own fork of it. It'll never go away. They're in control of it. If the original branch, uh the original project gets updated, they can pull those upstream changes in, but now they're sort of they're the masters in control of it. Watch out for misnamed concepts in code. And this goes from everything to from typographical errors like misspellings to concepts that totally had their name changed. Bite the bullet and rename them and don't keep using them with the wrong name
Speaker 1: because it just gets harder and harder as you go. Well I have a big regret. I worked on a project for like six months and uh we have this concept called boost. And at some point for a business reason that concept was named to the end user uh EasyBook. And then shortly thereafter, I passed the project on to someone else, and I never had the chance to go through and update all that code. And I know that poor guy was like getting these tasks and assignments like we need to update the easy book pages And he went to the code and it didn't exist and what the hell is all this boost stuff? So rename those things. It it makes life a lot easier. When you get sloppy code, you'll realize that Stuff like this is what made it really sloppy in the first place a lot of the time.
Speaker 1: Someone just didn't do the due diligence to rename that And then premature configuration. We talk about premature optimization a lot, and this is sort of a type of it. I've gotten code that never Never left a developer's machine. Um and never had any real work done. It wasn't in any way close to finished, but it was pre-configured to use like five different key value store backends and NoSQL databases and celery. We had no requirement for asynchronous tasks, but it was all there. And if you leave that in place, it always ends up getting in the way. It gets in the way when you try and do deployments. Sometimes uh developers will override um really obscure settings. That you don't expect to have been configured.
Speaker 1: And so weird things like that come up. The point is don't be afraid to throw all that out and start with fresh configuration on a really sloppy project. Um you have a copy of the original code. You can get it back at any point. Don't bang your head against the wall if you don't have to. So just to recap, write tests first. All dependencies introduce complexity, so review them. Organization is important. Monoliths. Every model is an app. There's a sweet spot in the middle. It's a broad area, but you want to be somewhere in there. Check all of the signals and receivers in the project. Make sure you know what they do. Same thing for context processors and middleware. Bad stuff usually hides there.
Speaker 1: And document the code as you go. Oh, and then lastly. Don't do any of these things and hand it off to someone else because it's just a prototype. Thank you.
Sloppy code often comes from pressure to deliver quickly, strict deadlines, corner-cutting incentives, and developers who do not expect to own the project long term. Rapid prototypes also commonly become production systems without receiving the redesign that a true prototype would normally get.
Discussed at 1:09Use an editor or IDE with code navigation, introspection, and refactoring support; a full debugger with breakpoints; coverage.py; and linting/style tools such as Pylint and PEP 8. Integrating these tools into the IDE makes feedback available while you work.
Discussed at 5:51Treat the existing project as untouchable initially: make a version-controlled copy or branch, get it running in its last known working configuration, create a disposable virtual environment, preserve or freeze its dependencies, and try to run the existing tests.
Discussed at 11:18Start with broad integration or smoke tests for every Django view and user path, then add unit or integration tests for business-critical logic such as billing, notifications, asynchronous tasks, and other core processes. The goal is to detect regressions and unexpected failures rather than to achieve perfect unit-test coverage immediately.
Discussed at 12:55Casey recommends aiming for about 95% coverage, with no individual files left substantially uncovered. Broad integration tests can make 90% coverage achievable quickly, but every file should have meaningful coverage before major changes begin.
Discussed at 16:43Rebuild the virtual environment with only the minimum known requirements, audit each dependency to determine whether it is unused, replaceable, or necessary, and use the test suite to catch anything you removed incorrectly. Once the dependency set is minimal, upgrade Django and other packages incrementally, testing after each upgrade.
Discussed at 20:35Create a sensible app structure, remove wildcard imports in favor of explicit imports, and move models first so dependent forms and views reveal what must move next. Run the tests throughout the migration; if the project has no production data, squashing migrations and starting over may be simpler, otherwise migrate the data carefully.
Discussed at 23:46Avoid using signals for business actions triggered by a user transaction, such as sending email, or for logic between models in the same application that can live directly in the model or view. Signals can hide important behavior and cause surprising side effects when developers manipulate the ORM, so all receivers should be located and audited.
Discussed at 28:32Move context-processor logic to template tags when it is only needed by particular templates, or into shared view base classes or mixins when it belongs to particular views. For middleware, ask whether the logic truly needs to run on every request or response—including requests from crawlers such as Googlebot—and redesign it if not.
Discussed at 32:20Document as you go instead of attempting a separate documentation project. A practical rule is that every code edit requires documentation: add docstrings to modules, functions, methods, and classes, and leave comments wherever complex transformations or ambiguous logic required investigation.
Discussed at 35:50Look for hard-coded URLs, broad exception handlers such as `try/except/pass`, unstable Git or GitHub dependencies, misnamed concepts, and configuration for infrastructure the project does not actually need. Rename misleading concepts and fork externally hosted dependencies into an owner-controlled repository.
Discussed at 37:27Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 14, 2026