Nested Loops Are Making Your Code Unreadable

ArjanCodes · Beginner ·⚡ Algorithms & Data Structures ·2mo ago

Key Takeaways

ArjanCodes discusses how nested loops can make code unreadable and demonstrates a real example of refactoring code to improve design and readability, focusing on algorithmic design principles and best practices for coding.

Full Transcript

You've probably seen code like this. Basically a bunch of nested loops and if statements. Now this code does work. As you can see both Alice and Bob placed one order and spent some money, which is the information that I want to know. But at the same time even though it works this code is of course really hard to understand. So how do you end up with code like this? You you might think that this is often some algorithmic issue. You know, let's rewrite the algorithm to something simpler or smarter and more efficient. But actually the problem is that these these nested loops that you see here they're really the root of the issue. It's often a symptom of bad design. And we're going to look into that today. And by the way, this is also exactly what I go deep into in my brand new program software design mastery. This is not just another course with patterns and principles. It's a comprehensive system for thinking about design, architecture, and most importantly tradeoffs. If you want to become confident in your design decisions, join the waiting list now at Arnold codes {slash} mastery. Link is also in the video description. The first question to ask yourself if you're dealing with code like this is is actually the right data structure used here. If you look at this part in particular, we have for customer in customers and for order in orders. That is actually a red flag. Basically you're scanning all the orders for every customer. What we're basically doing here is a manual database join. So the fix is not to make these loops more clever, but model your data better. Of course if you store these things in a database, then instead of these for loops you can replace it by a join. But in this particular example, we just have some data that's hard coded right here. So what you can then do is create a function, let's say group orders by customer to do that join for us. So, what do we have here? Well, we have a list of orders. And as a result, we will get a dictionary that maps a customer ID, which if we look in the order, that's an integer, to a list of orders belonging to that customer. So, let's first create this dictionary like so. And by the way, you might wonder, "Hey, why is Arya not using AI to type all this for him?" Well, the reason I'm not doing that is that I think educationally speaking, it's better if I actually type it up. The videos are better to follow, and I also got that back from you in the comments. Unfortunately, now I get comments that, "Hey, why is this guy not using AI? What is this? 2011?" No, it's not 2011. It's 2026, or I don't know when you're watching this video. Anyway, that's why I'm not doing it. I am using AI, but in the videos, I'm not using it. All right? Good. Anyway, orders by customer, that's the dictionary that we're going to fill. So, what we'll do here is we will just go through all of the orders. And then orders by customer, I am I am allowed to use auto complete, right? I hope so. Orders by customer, let's set a default value for order.customer_id to the empty list. And then we're simply going to append the order to that. And finally, we are going to return orders by customer like so. So, now that we have this, that removes one of these nested loops. Because now what we can do is first get the orders by customer, which will be this, like so. And this is now a dictionary mapping a customer ID to orders. And what we can do now is customer orders equals orders by customer get customer.id and default that list is going to be empty. And now we already no longer need this. I can de-indent this, like so. That already simplifies the code. Now, we still have this nested loop here, but this already simplifies things a lot. The second thing that you should consider when you're dealing with code like this is whether your behavior is actually in the right place. And I'm not so much talking about responsibilities, but more about the level of the thing that you're doing. For example, here, well, we have a pretty high-level function generating a customer report that gets customers and orders. But, if you look at this, for example, it calculates the order total. And that is actually a really low-level order logic, order operation that should not be in a piece of code that generates report. This belongs to the order. So, asking yourself the question, is all this logic happening on the same level or are there some things that don't really belong here? That often already simplifies the design a lot. In this case, we want to move this bit out of the report generation code because it simply doesn't belong here. So, I'll just copy this over and I'm going to the order class, and there, let's add a property for this. And that's basically this. But, actually, what we can do now is use a list comprehension. So, we're going to return the sum of item.price * item.quantity for items in self.items. I remove this. And since we're dealing with decimals, we will also need to provide a default value here of, let's say, zero. Like so. And of course, this should be called item. And now in our customer report, we don't need this for loop anymore. We can simply write order. total. And we don't need this anymore. How do you decide whether something is low-level behavior? Well, in general, if behavior only needs data from the object, so in this case, if you look at order.total, it only needs to know the item price and quantity for each of these items. That's the only thing. Well, then it probably belongs to the object and not in the function where you're writing that code. And as you can see, the loop now also already becomes much simpler because it stops doing that kind of low-level work. Now, another closely related issue is too many responsibilities. So, even if you have code like this, which is already relatively short, there is still a lot of responsibility in this particular function. It finds the orders, it filters the paid ones, it calculates the totals, it applies discounts. That's the bit that is happening here. It builds the output. That is what is done here. And that is actually a already way too much for a single function. How do you determine whether this is too many responsibilities? Well, what I like to do is think about reasons to change. So, what are some reasons for this function to change? Well, if the discount logic changes, I need to change this function. If the order structure changes, if status is stored somewhere else, I need to change this function. If my summary data changes, I need to change the function. So, there's many reasons for this function to change. And in terms of responsibility, you want to make sure that basically every function, every method has one main reason to change. That's a good way to think about it. So, you don't always have to go all the way, but splitting it out a bit to reduce those responsibilities already helps a lot. For example, we can identify a few concepts here that we could maybe separate, like uh filtering on the paid orders only, or applying the discounts, or building the customer summary, which is what is happening here. So, let's do that. We can write a function paid orders. This gets a list of orders. And this will also return a list of orders. So, this is a filter function. So, we use list comprehension here. Order for order in orders if order dot status equals paid. Like so. And now, we can create our paid orders here by filtering the orders for the customer. And let's not call that the same as the function to avoid confusion. This is what we'll now iterate over. And we have another if statement that we can now remove. So, now we have this. Let's also define a function apply discount. And this is going to get a customer. And it will get a total. And this is where we're going to put this code. Like so. And otherwise, we're simply going to return the total. Finally, we can create a function that does this last bit. So, we have build customer summary. This gets customer. Let's orders. And this will return a customer summary. So, this basically does the work for a single customer. And we can now use these other functions. So, what we can do is compute the totals, which is apply discounts on the customer and order dot total for orders in base orders. Like so. And then we can return the customer summary, which is here. Like so. So, this needs to be length of the totals, and total spent is the sum of the totals. And default is going to be zero. There we go. Now, generating our customer report becomes way simpler. So, we don't need to store all of this anymore. We now simply get our customer orders, and then we can create our summary by passing the customer And the customer orders. And we simply add that summary to our reports. And all of this is now taken care of by the other functions. Like so. Let's see if this still runs as expected, and it does. Now, this is way better. We have these separate functions that help give our logic name. We know what the logic is actually doing. It almost acts like a comment in your code, right? It makes it a lot easier to understand. Now, of course, in this final version, we didn't really fix the nested loops. We didn't try to remove all the nested loops. Uh they're still there, but they're kind of hidden in uh different functions that still have for loops or these list comprehensions or these uh sums that we have here. So, they're still there. The loops are still there. But, at the same time, we now have functions that are way simpler and don't have mixed responsibility. It's really clear what this code is actually doing. So, we've looked at the structure of the data, whether the behavior, the logic is in the right place. We looked at whether there is not too many responsibilities in a single place. Now, there's a few other design problems that nested loops can point to. But, if you enjoy breaking down messy code like this and turning it into something that's more understandable, like this video, subscribe to the channel. It helps my channel grow and also tells me to keep fixing code like this. Now, what are some other reasons you may encounter? One is a wrong abstraction level, where in essence, instead of working with objects like this, order, order item, customer, you're working mostly with primitives. So, you're passing around a bunch of integers and strings and floats and decimals and things like that. If you do that, that means that you're going to get very messy code because you have no idea what the data actually is. You don't define that. That can also lead to nested loops where you're going to have a list of a list of order items or something like that. Related to that is a fear of introducing some extra structure. So, if you avoid dictionaries or classes or tuples at all costs, you your control flow is going to be more complex. Another reason why this kind of complicated code appears is basically incremental growth. You just add one more condition, one more loop, and before you know it, your code is going to be deeply nested. So, it is not something that you look at once when you're like done writing your script. It's something you always keep in the back of your mind. Another reason is you may not know certain Pythonic tools, like comprehensions for example or the sum functions. These are very useful. So, if you see nested loops and complicated conditions in your code, don't just try to optimize them or try to get rid of them. No, think about what design problem they may be hiding. Ask yourself, am I using the right data structure here? Am I doing low-level work that doesn't belong here? Am I doing too much in one place? Does this function have too many responsibilities? If I change something, do I need to change this function as well? Can I Can I split things up more? So, in essence, if you see a nested loop, it's not particularly bad. The nested loop isn't the enemy. It's more of a signal that there might be something wrong with your design. And if you fix the design, these loops also usually fix themselves. Even though they may still be there because, well, you may need to iterate over things, and that's not a problem. But if you organize it well, it's going to be much easier to work on. Now, I'd like to hear from you. What do you think? When you run into nested loops like this, what What is usually the cause in your code? Let me know in the comments. Our YouTube thinks you might like this video next. Thanks for watching, and see you next time.

Original Description

🧱 Build software that lasts. Join the Software Design Mastery waiting list → https://arjan.codes/mastery. Nested loops are often treated as an algorithm problem. But most of the time, they’re a design problem. In this video, I break down a real example and show why nested loops usually appear: wrong data structures, misplaced behavior, and too many responsibilities in one place. Then we refactor the code step by step into something simpler and easier to understand. 🔥 GitHub Repository: https://git.arjan.codes/2026/nested. 🎓 ArjanCodes Courses: https://www.arjancodes.com/courses. 💬 Join my Discord server: https://discord.arjan.codes. ⌨️ Keyboard I’m using: https://amzn.to/49YM97v. 🔖 Chapters: 0:00 Intro 1:09 Symptom 1: Wrong Data Structure (Implicit Join) 4:33 Symptom 2: Behavior in the Wrong Place 6:54 Symptom 3: Too Many Responsibilities 12:45 Other Reasons Nested Loops Appear 14:13 Final Thoughts #arjancodes #softwaredesign #python
Sign in to unlock AI tutor explanation · ⚡30

This video teaches how to identify and refactor nested loops to improve code readability and maintainability, using a real example to demonstrate the process and its benefits.

Key Takeaways
  1. Identify nested loops in your code
  2. Understand the design problem they pose
  3. Refactor the code to reduce nesting
  4. Apply algorithmic design principles to improve readability
  5. Test and iterate on the refactored code
💡 Nested loops are often a design problem rather than an algorithmic one, and refactoring them can significantly improve code readability and maintainability.

Related Reads

Chapters (6)

Intro
1:09 Symptom 1: Wrong Data Structure (Implicit Join)
4:33 Symptom 2: Behavior in the Wrong Place
6:54 Symptom 3: Too Many Responsibilities
12:45 Other Reasons Nested Loops Appear
14:13 Final Thoughts
Up next
Webhooks & Callbacks For Beginners in Python
NeuralNine
Watch →