I have many times encountered the statement from developers that “90% of time is spent reading code”, and therefore readability is a very important quality of code. So important, in fact, that it can overtake other qualities. This is almost always used to justify less abstract code. Highly abstracted code can be “hard to read”, so much so that it’s positive qualities (less duplication, more scalable and maintainable, etc.) may be invalidated by the fact it is just so hard to read. More “straightforward” code, perhaps with the lower level details spelled out explicitly in line may suffer problems of maintainability, but since we spend most of our time reading it, not making changes to it, the less abstract but easier to read code may still be better.
Since you hear this exact phrase (the 90% line), it’s obviously coming from some influential source. That would be Uncle Bob’s book, Clean Code: A Handbook of Agile Software Craftsmanship:
Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code. …[Therefore,] making it easy to read makes it easier to write.
Now, he’s saying improving readability improves modifiability. But these discussions end up becoming about readability over modifiability. I’ll dig into why that matters.
Excuses for poorly abstracted code are abundant, particularly in “agile” communities. The first one I encountered was spoken by the first scrum master I ever had. Basically, since agile development is about rapid iteration, thinking design-wise more than two weeks ahead is wasteful, and you instead just want to focus on getting a barely working design out quickly, to complete the iteration before starting the next one.
Then, I heard more excuses later, ranging from YAGNI to “junior developers can’t understand this” to arguments about emergent design and “up-front design is bad” (the implication being that writing highly abstract code always counts as upfront design). There are a lot of misconceptions around this topic, especially the idea that building up a runway of library tools (i.e. abstractions) that can be quickly and easily composed into new features somehow hurts your ability to quickly implement new features with short turnaround (in other words, agility), but that’s not what I want to talk about here.
I just want to talk about this argument that “code should be easy to read”, implying that some other quality of it should be sacrificed. The reason I bring this other stuff up is to put it in context: developers are constantly looking for excuses to write less abstract code, so the “readability” argument isn’t unique at all. It’s just another of many avenues to reach the same conclusion. You should view these arguments with a bit of suspicion, as all of these unrelated premises land at the same place. Perhaps the conclusion was foregone, and people are working backward from it to various starting points.
(The simple, banal, reason developers look for excuses to not write abstract code is because abstracting is a form of low time preference: investing into the future instead of blowing all your money today, or running your credit card up. Investment requires delaying gratification, which is unpleasant here and now. It’s the same reason everyone wants to be fit but no one wants to go to the gym.)
For transparency’s sake, I’m the one who thinks abstraction, lots of it, is literally the only way any of us can begin to comprehend modern software. If you don’t believe me, remove all the abstractions and read the compiled code in assembly. This is why whenever I see a principle like YAGNI or readability used to conclude “un-abstracted code is better, actually”, I become extremely suspicious of the principle. At best it’s being completely misunderstood. Sometimes on closer inspection the principles turn out to be nonsensical.
I think that’s the case with “readability”. It doesn’t make sense as a principle of good software design.
Readability Is Largely Subjective
Let’s start by assuming the statement, “90% of time is spent reading code” is correct (I’m not sure how this could ever be measured… it feels accurate enough, but giving it a number like it was scientifically measured is just making it sound more authoritative), and let’s assume that this straightforwardly implies readability is 10 times more important than other qualities of the code.
I’m going to say something, the exact same thing, in two different ways. The only difference will be the phrasing. But the meaning of these two sentences are exactly equivalent:
- This code is hard to read
- I have a hard time reading this code
A skill I think everyone should learn is to pick on the habit of people using existential phrasing (sentences where the verb is “is”) to take themselves out of the picture. People will share their opinions, but instead of saying “I like Led Zeppelin more than the Beatles”, they say, “Led Zeppelin is better than the Beatles”. See the difference? By removing themselves as the subject, they are declaring their subjective judgements are objective traits of the things they are judging.
It’s not that you find this code hard to read, which is a description you and your reading abilities, it’s that the code is just hard to read, like it’s an innate quality of the code itself.
The implication is that all developers will always agree on which two versions of code is more readable. Hopefully I don’t have to emphasize or prove with examples that this is nowhere close to the truth. This is what leads to advice from developers that’s basically tautological: “you should write code that’s easy to read”. Who’s intentionally writing code that’s hard to read? This is like telling someone, “you should be less lazy”, as if anyone gets up in the morning and yells, “WOO, bring on the laziness!” Yeah, I know being lazy is bad, do you have any techniques for overcoming it, or just empty platitudes?
With the “readability” argument, developers are claiming, whether they realize it or not, that certain developers are consciously eschewing readability in favor of something else, like the DRY principle. Maybe some of them do, but that’s rather presumptuous. Why do you assume those developers don’t actually find the DRY code easier to read and understand? Maybe for them, having to see nearly identical blocks of code in multiple places, realizing the only difference is the name of a local variable, and reverse engineering in their head that it’s the same code and it’s doing something, and after looking at it for a moment they see it’s doing X, makes code harder to read than seeing a single line that’s a call to the function
doX(someLocalVariable). Maybe they introduced the abstraction because, in their opinion, that improves readability.
Are you assuming they consciously rejected readability in favor of something else simply because you find it harder to read? Why are you assuming if you find it harder to read, everyone else must too?
Example 1: I have a book in my hand that’s written in Spanish. Embarrassing as this is, I’m not a polyglot. I only speak and read English. The Spanish classes I took in college didn’t take. How hard is it for me to read the Spanish book? Well, straight up impossible. I just can’t read it, at all. A native Spanish speaker, on the other hand, might find it quite easy to read. Or, if I bothered to learn the language, I could read it too.
Example 2: I spend more time than I’d like to admit in public reading physics textbooks. For me, an undergrad or grad level textbook on classical mechanics is an easy read. I haven’t read fiction since middle school. Any moderately sophisticated adult fiction book would probably leave me lost trying to figure out the metaphors and recall tiny plot hints that were dropped chapters ago that explain what I’m reading now. Maybe I’d figure it out, but I wouldn’t call it easy. Contrast this with, well, normal people, and I’m sure it’s the exact opposite: they breeze through the fiction, picking up on all the literary devices, and can’t get through the introductory math section of the physics textbook.
Now, some of this is difference in personality. I’m the kind of person who gets and likes math, and doesn’t really care about fictional stories, some people are the opposite. But a lot of it is practice. I find physics literature easy to read because I’ve spent a lot of time practicing. That grad level textbook was not easy the first time I read it, not even close. But it got easier each time I read it. The next one I read was easier because I already had experience. I would probably get better at reading fiction if I ever practiced it.
You see my point, right?
You can’t read highly abstracted code because you aren’t used to reading it.
It’s not an innate quality of the code. It’s a quality of you. The code doesn’t read itself.
I am not implying there are no objective qualities of code that would affect anyone’s ability to read it. The author could have scrambled the words into a random order. Yeah, that makes it harder to read for everyone. This is irrelevant because no one does this. No one sets out to write something that’s hard to read. If you suggest a change that everyone agrees, including its author, makes it easier to read, it’s going to be accepted with no resistance (the author probably just didn’t think of it), and “more readable code is good” didn’t help reveal that change (it’s an empty tautology). If the suggested change is controversial, it’s quite likely some people think the change degrades readability.
This is why “readability” can’t be a guideline for development teams. Either team members are going to disagree on which code is actually more readable, or they agree code is hard to read but can’t think of how to make it better. Telling them “make it more readable” doesn’t help in either case.
This applies not just to the level of abstraction, but the style and tools. Someone who’s never heard of ReactiveX would probably be totally bewildered by code written by someone who thinks “everything is an
Observable“. But it’s easy to read for the author, and to anyone else who has years of experience reading that style of code.
My point is not that there’s no such thing as bad code. Of course not. It’s just that whether code is high or low quality has nothing to do with whether any particular developer is good at reading it or not.
We have the fortune of not having to learn to read assembly code. Coders in the 1960s, or even 90s, weren’t so lucky. Super Mario World was written in assembly. The authors were probably so good at reading assembly their brains would recognize control flow structures like loops and subroutines. They didn’t just see unrelated blocks of code, they saw Mario jumping around and hitting scenery and launching fireballs.
You’ll find highly abstracted code easy to read if you spend more time reading it, as long as you don’t approach it with a cynical attitude of “this is stupid, I shouldn’t have to put up with code like this”. Again, I’m not saying you have to like that code. It may be the worst code you’ve ever read. And you can still make it easy to read.
It’s somewhat ironic that I’ve spent enough of my career reading code I think was pretty poorly designed when I have to work on “legacy” apps, that I’ve gotten pretty damn good at reading bad code. It’s bad, but there’s a lot of it out there so it’s necessary for my job to be good at reading it.
How to Read Abstracted Code
I’m aware of a few differences in the way I read code compared to how I think developers, who especially don’t like highly abstracted code, read it. They are probably relevant to why I have an easier time reading such code.
The absolute biggest one is: I don’t feel a need to “click into” an implementation, or class, or other abstraction and read its implementation details simply because I encountered a use of it in code. If I ever do feel that urge, it immediately turns into an urge to rename something. The name should make it self-evident enough what role it’s playing in the code that’s calling it.
I should not need to open up a coffee machine and look at how all its gears and pumps are hooked together to know that it’s a coffee machine, and pressing the “brew” button is going to make coffee come out of it. But if it’s just an unlabeled machine with a blank button it, okay maybe I’d want to open it up to figure out what the hell it is. I could also just press the button and see what happens (for code, that means find the test that calls this method or instantiates this class, and if doesn’t exist, write it). Once I figure out what it is, I make sure to label it properly, so no one has to do that again.
The theme here is: when you organize a large codebase into modules, and you’re still clicking from one module to another while reading, or even worse stepping from one module to another during debugging, you’re doing it wrong. Want to challenge yourself? Put your library modules in different repos and consume them in your app through a package manager, in binary format. No source code available. You shouldn’t need a library’s source code while you’re working on the app that uses the library, and vice versa. This will also more or less force you to test the library with tests for the library instead of testing it indirectly through testing the app.
This is the single biggest complaint I’ve gotten from code I wrote for large applications with dozens of very similar screens. No, I’m not f**cking copy-pasting it 25 times. That’s a maintenance nightmare. But the complaint is, “I have to click through dozens of files just to get through a single behavior”. No, you don’t have to, you just do. Have you considered not clicking through? Have you done some self-reflection on what drives your need to dig through implementation details of implementation details to understand something? You usually can’t do that once you hit the platform SDKs. It’s not like there isn’t implementation details inside those classes, it’s just not publicly accessible. If you can stop there, why can’t you stop on abstractions in your source code?
The answer might be, “because they’re bad abstractions”. They’re badly named, or worse yet leaky, un-cohesive, slice an actual abstraction into pieces and scatter it around multiple files, combine two unrelated functionalities and select one with a parameter, etc. Yes, developers can absolutely do a bad job of abstracting. The solution is not to destroy the abstractions, it’s to fix them. This is why you need to ask yourself why you want to click into everything. There may be a good answer, and it may lead to an improvement in the code that’s a lot better than just inlining implementations everywhere.
Clicking into stuff isn’t always bad. But you need to treat it as a context switch. You unload everything from your brain you were reading before you clicked in. It’s irrelevant now. How you got to a method or class or whatever should have nothing to do with what that method or class is doing. If the meaning isn’t clear from just looking at the method or class, it needs a better name, or it’s a bad abstraction if it really requires context from the call site to understand.
How did I learn to read code like this? Practicing. The most reliable way to get better at X is to practice X. Do you want running a marathon to be easy? Then run some marathons. The first few will be really, really hard. They’ll get easier. Want to learn to play a musical instrument? Pick up the instrument and start playing it. It will be really hard at first. It will get easier.
Just because you can get better at reading a certain style of code doesn’t mean you should. I’m sure it’s a waste of time for any of us to get as good at reading assembly as the Super Mario World programmers were. It’s not a waste of time to get good at reading highly abstract code. Change it as soon as you can if you want, but you’ll only get faster at that if it’s easy to read.
Does Optimizing for Readability Even Make Sense?
Why cite the actual number for how much time we spend reading code? It’s 90%, apparently. The reason is because we humans really like simple relationships. Ideally, linear and proportional ones. We like to believe that if we can measure the proportions of time allocation on different activities, we can straightforwardly calculate the weights for different optimizations. If we spend X1% amount of time doing Y1, and X2% of time doing Y2, then optimizing Y2 will be X2/X1 times as important as optimizing Y1. We love these kinds of simple calculations.
This is why I never hesitate to remind people that physics problems become literally impossible to solve analytically as soon as you introduce… wait for it… three bodies. The number of problems in the world that are actually solvable with these kinds of basic algebra techniques are miniscule. We are heavily biased toward simplifying real problems down until they become solvable like this, destroying in the process any relevance the now toy model of the problem has to the real problem.
There are a lot of unspoken assumptions between the statement “we spend 90% of our time reading code and 10% of the time modifying it” and “optimizing readability is 10x more important than optimizing modifiability”, or even that optimizing readability is important at all. I’m going to try to bring out some of them, and we’re going to find they aren’t justifiable.
First, let’s clarify what exactly we’re trying to “optimize”, overall. Hopefully, as professional developers, we’re not merely trying to optimize for our own comfort. Of course making our jobs more pleasant is good for the profession, because we’re more eager to do our jobs, and we’re people who work to live, not faceless cogs in a corporate machine. But they’re still jobs (not, as Red Foreman says, “Super Happy Fun Time”). At the end of the day we’re being paid to deliver software.
It doesn’t matter if we spend 99.99999% of our time reading code.
We aren’t paid to read code.
We are literally paid to modify code. The only reason the reading is valuable at all is because it is a necessary precondition to the eventual modification.
Thus, optimizing for being happy during the 90% of our jobs when we aren’t actually producing deliverables is extremely dubious.
This is literally in Uncle Bob’s quote: that the reason improving readability is important is insofar as it improves writability. He was not pitting the two against each other.
But this problem is being framed as a “readability vs. modifiability” problem (some sort of tradeoff). Any adjustment of code that makes it simultaneously easier to read but harder to modify is absolutely detrimental to productivity, because the only actually productive phase is during modification. The only possible way readability could help productivity is because improving readability improves modifiability.
If you find it way easier to read less abstracted code, but directly at the expense of making it more complicated to modify it appropriately, how is this improving productivity? I totally get that it’s making your day more pleasant, since you’re spending 90% of that day reading instead of modifying the code. But is that what we’re optimizing for? You not tearing your hair out because you hate your job so much? I’m not being totally facetious. It is important for productivity that you not want to go postal every time you open your IDE. But this can’t come at the expense of you getting stuff done. You need a way to be happier and more productive.
A way to do that is get used to reading the code that’s driving you crazy. Over time it won’t drive you crazy. At worst you’ll start finding it funny.
Honestly I don’t think readability and modifiability are in competition very often, if ever. I think it’s a false dilemma, as I explained above, born out of developers being presumptuous that all other developers share their opinion about what code is easiest to read. Developers assume the person who wrote the code they’re staring at and finding hard to read was written by someone who agrees it’s hard to read but believed readability and some other quality (which is going to be some aspect of modifiability) were in competition, and he let the other quality win. I think it’s way more likely the author believes his version of the code is both easier to read and easier to modify, and the current reader simply disagrees with him about that.
Is It Good We Read Code This Much?
Another unspoken assumption here is that the ratio of reading to modifying is constant. Not just constant, but specifically independent of the ease of reading and modifying code. No matter how much the ease of either of these activities change, the assumption is made that the reading/modifying ratio will stay at 10/1. I mentioned above that, this number was probably pulled out of someone’s nether regions, and is completely meaningless (87.3% of all statistics are made up on the spot). Given that we’re just making up numbers, the only thing we could do if we wanted to model how this made up number is coupled to the other variables we’re thinking about modifying is to make up more facts about how this coupling works. But that’s still better than just assuming there’s no coupling at all.
It seems pretty natural to me that there should be some rather strong coupling between the reading/modifying allocation ratio and how easy/pleasant both of these activities are. I would assume that making code much, much easier to modify is likely to lower the read/modify time ratio. Now that’s interesting. Remember again that, if we apply productivity multipliers to both activities, modifying gets a full 100% (actually that’s too simple, sometimes it can be negative, more on that below), and reading gets a big fat goose egg (again, not implying you can optimize reading out of the equation, it’s indirectly productive). In terms of productivity then, anything that decreases the read/modify time ratio is literally increasing the amount of actual productive time. That cannot be ignored. It could also disproportionately reduce the rate of productivity during modification (“velocity”), so this doesn’t necessarily imply increased productivity.
So, perhaps it’s true that today developers typically spend 90% of their time reading code, and only 10% of the time modifying it. And maybe that’s a problem. Maybe that’s because most of the code bases we’re working on today are huge piles of ten-year-old spaghetti following a design that could barely scale to a 9 month old app, and we can’t help but stare at in disbelief for hours before finally mustering up the courage to edit one line and see if it actually accomplishes what we’re trying to do. Maybe we’re in this situation because so much code out there is a total maintenance nightmare, we can’t figure out how to do a 10 minute modification without rereading a chunk of code for an hour and half first. Maybe if we all wrote code with ease of modifiability as the primary goal, we wouldn’t need to preamble the next modification with such a massive amount of preparatory study.
Or maybe not. Maybe this is just how it goes, and it’s unreasonable to think it could be any different. But there’s a wide range of options here. It could be that spending any less than 90% of time reading code is just unreasonable, or it could mean that spending less than 50% of time reading code is unreasonable, but we’ve got a solid 40% of potential improvement to work with. Or somewhere in between.
A large amount of my time is spent neither reading nor writing code but simply thinking about the problem. I really believe this is the most crucial phase of my job. Software engineering is more creative than formulaic, it’s much more like architecture than construction. The most valuable moments for the businesses I work for are random epiphanies I have in the middle of a meal when I realize a certain design is perfectly suited for the new requirements, and this is followed by a three day bender where I’m almost exclusively writing code. The time allocation for this is kind of nonsensical because it is often just background while I’m doing other stuff.
Does time allocation really even matter for productivity? Recall that if you’re optimizing a software algorithm, when you find that 90% of time is spent on one part, your goal is to reduce that number.
Quality vs. Quantity
Readability is subjective. But there are objective qualities of writing that virtually everyone will subjectively agree makes it harder to read.
What’s easier for me to read? A press release or a Top Secret classified document? Okay, sure, the former is easier to read, by simple virtue of it being available to me. That’s not an accident. I’m not supposed to read the classified document.
Now, imagine your friend complains that a novel is hard to read. You inquire further, and determine she is frustrated that the novel doesn’t come with the author’s notes and early drafts added as an appendix. She exclaims, “how can I know what the author meant here if he’s not going to share his notes with me!?”
Okay… if you’re having trouble reading a novel because you can’t read the author’s notes about it, there’s something wrong with the way you’re approaching reading the novel. That extra stuff is intentionally not part of the book.
Now imagine your other friend is complaining a novel is hard to read. You inquire further, and find he is jumping around, reading all the odd chapters and then reading all the even chapters. He reads Chapter 1, then 3, then 5, and so on, then circles back and reads Chapter 2, then 4, etc.
After lamenting to yourself how you managed to find all of these friends, you ask him in bewilderment why he isn’t just reading the damn thing in order. The order is very intentional. Maybe the novel isn’t chronological, that’s very important to its structure, but your friend thinks all stories should be presented in chronological order and is jumping around trying to reorganize it in that way.
If your teammate wrote code you find hard to read, it’s quite possible your teammate finds it easy to read. It’s also possible that however you’re trying to read it, the author made that harder on purpose.
Reading code is only indirectly productive, and insofar as it aids in future modification, which is directly productive. It is also the case that not all modification is productive. Correct modification is productive. Incorrect modification (introducing bugs) is counterproductive. That’s even worse than reading, which is merely (directly) unproductive.
Another tendency I see among some developers is to be focused on how overall easy it is to make changes to code. This is definitely a big part of the arguments I hear in favor of non-compiled (basically not strongly typed) languages. Having to create types is hard, it makes every little change harder.
And changing is what we get paid for, so making changing code harder is bad, right?
F*** no it isn’t.
It isn’t if most changes are actually harmful.
It’s way, way easier to break code than it is to fix it or correctly add or modify functionality. Thus, most changes to code are actually harmful.
If you’re into XP, you love automated tests, right? TDD all the way right!? All a test does is stop you from modifying code. It does nothing but make editing production code harder. That’s why some developers hate test suites.
In my experience, high quality code is high quality not so much because it makes correct modification easier (this is the #2 aspect of high quality code) but because it makes incorrect modification harder (this is the #1 aspect). Thus, when I hear developers complaining that working on this code base, given its design, is too hard, I’m suspicious that they’re complaining their bugs are being found as they’re being typed instead of later by QA or customers.
Is it “easier” to read the internal details of a library class that are exposed to you than it is to read those that are encapsulated? Well, yeah. But that doesn’t mean you should expose everything as public to make it “easier” to find. Hiding stuff is a fundamental tool of improving software quality.
If “easier to read” means less abstraction, and all the implementation details spelled out right there so you don’t have to click around to find them, then this literally means less encapsulation. Less intentional hiding of stuff you might want to read but you damn well shouldn’t be because all reading it is going to do is make you want to couple to it.
Some parts of code need to be easy to read and understood by certain people in certain roles. Other parts of code, those people in those roles have no business reading it and doing so is likely to just confuse them or give them dangerous ideas. This is directly relevant to the way “readability” is often connected directly to abstraction.
Abstraction introduces boundaries, and intentionally moves one thing (an implementation detail) away from another thing (an invocation of the thing being implemented elsewhere) specifically because it is not helpful to read both at the same time. Doing so only feeds confusing and misleading ideas that they are relevant to each other, that they should be colocated, are likely to change together, that they form a cohesive unit, and have no independent meaning.
Highly abstract code does intentionally make certain approaches to reading code harder, as a direct corollary to it making certain approaches to modifying code harder. Properly abstracted code makes the modifications, plus the reading that likely precedes those modifications, that are not helpful and degrade the code quality, harder. This is a feature, not a bug.
The structure may be stopping you from even reading the code in a way that leads up to you making a damaging modification, especially if the main problem you’re having is that you’d have to punch through a bunch of encapsulation boundaries to do whatever it is you’re trying to do.
The overall thesis here is simple: readability has no simple relation to code quality. Readability has a large subjective component, and to the extent authors intentionally frustrate certain approaches to reading code, there can be valid reasons for doing so.
Even examples I try to come up with that I think are as objectively more readable as can be, like naming a variable
thisSpecificThing instead of
x, I know someone is gonna say, “no
x is better because it’s more concise, the more concise the easier it is to read”. I can’t argue with that. I can’t tell someone they don’t like reading something they like reading. If I want to convince him to please name his variables descriptively, “more readable” isn’t a way to do it.
If you have trouble reading highly abstract code, you just need to practice reading it, which will help you understand why it discourages certain reading strategies. It’s also really helpful to practice writing it. If you always tell yourself you don’t have time or it’s not agile enough, when will you ever get this practice?
There’s nothing intrinsically unreadable about even extremely DRY’d heavily templated and layered code. It may even be excessive or inappropriate abstraction (I don’t like saying “excessive”, what matters is quality, not quantity), but even that code can be easy to read if you just practice doing so. Then you’ll get really good at improving it.
The rules of thumb for achieving high code quality need to be objective, not subjective. Examples of subjective (or at best tautological, they’re all fancy synonyms for “good”) qualities are:
- “Clean” (what does this mean? You wiped your code down with Lysol?)
Examples of qualities that are objective but so vague that they aren’t functional rules of thumb on their own (they are rather guiding principles for producing the rules of thumb) are:
Examples of objective rules of thumb are (you may agree or disagree if these are good rules, I’m just giving examples of rules that are objective):
- Everything is
- Use value semantics until you need reference semantics
- Always couple to the highest possible level of abstraction (start with types at the top of the hierarchy, and cast down only once needed)
- Law of Demeter (never access a dependency’s dependencies directly)
- Use inheritance for polymorphism, use composition for sharing code
- Make all required dependencies of a class constructor parameters (don’t allow constructed but nonfunctional instances)
- Use a single source of truth for state, and only introduce caching when performance measurements prove it necessary
- Don’t use dynamic dispatch for build-time variations
- Use dynamic dispatch over procedural control flow
- View should only react to model changes and never modify themselves directly
You see the difference right? Good or bad, these rules actually tell you something concrete. It’s a matter of fact whether you are following them or not, rather than subjective preferences.
The first category, in my opinion, is useless. The second category is useful only as a means of generating statements in the last category. It’s the last category that should go in the style guide of your README.
Being in the first category, I think developers should stop talking about readability. I mean, if they just want to complain, confiding with their fellow team members how frustrating their job can be, that’s fine. But citing “improve readability” as any kind of functional advice or guiding principle… it’s not. And if you have trouble reading code, reframe that as an opportunity for you to grow. After all, we’re software engineers, our job is to be good at reading and understanding code.