DEV Community

Cover image for I Deleted 200 Lines of Code I Didn't Write and Learned More Than When I Wrote It...

I Deleted 200 Lines of Code I Didn't Write and Learned More Than When I Wrote It...

Gamya on July 09, 2026

Quick note before we dive in — I know I've been off track from the iOS/Swift series lately. I just have so many thoughts I need to get out of my he...
Collapse
 
alexshev profile image
Alex Shev

Deleting unfamiliar code is one of the fastest ways to learn the real architecture. Writing code teaches the intended path; removing code tests whether the path was actually necessary. The scary part is that the best refactor often looks like doing less, which is harder to defend than adding another abstraction.

Collapse
 
gamya_m profile image
Gamya

"Harder to defend than adding another abstraction" is so true and I think it's why so much unnecessary complexity survives — adding something looks like work, removing something looks like you didn't do anything, even when the removal required more understanding than the addition ever did. The deleted code post literally involved an afternoon of careful reading and reasoning and the output was fewer lines, which is a very hard thing to point to in a standup as evidence of productivity. But the codebase is cleaner and I actually understand it now, which is worth more than whatever I would have shipped in the same afternoon by generating more.

Collapse
 
alexshev profile image
Alex Shev

Exactly. Removal is hard to defend because the visible artifact is absence. The work shows up later as fewer branches to reason about, fewer assumptions to preserve, and fewer places where a future change can break. But in the moment, it can look like negative productivity unless the team values understanding as an output.

Thread Thread
 
gamya_m profile image
Gamya

"Understanding as an output" is the framing that most teams don't have language for, which is why removal is so hard to defend in the moment — the usual outputs are features, fixes, or improvements you can point to, and understanding doesn't show up on any of those lists even though it's what makes all of them possible. The absence artifact problem is real too. Fewer lines, fewer branches, fewer assumptions to preserve — all of that is genuinely valuable and completely invisible until the moment someone needs to make a change and it goes smoothly instead of being a week-long archaeology project. The value is in what doesn't happen, which is the hardest thing to get credit for.

Thread Thread
 
alexshev profile image
Alex Shev

That is the missing vocabulary. Teams can celebrate output they can demo, but they rarely count understanding as something delivered. Yet a codebase with fewer branches, fewer rumors, and fewer forbidden zones is very real progress. It just needs a better artifact than a line-count delta.

Thread Thread
 
gamya_m profile image
Gamya

"Better artifact than a line-count delta" is the unsolved problem honestly — because the value is real but the representation is missing, and without a representation it doesn't show up in sprint reviews or retrospectives or anywhere decisions about how to spend time get made. A codebase with fewer forbidden zones and fewer rumors is genuinely more valuable than one that shipped two more features at the cost of two more load-bearing mysteries, but there's no metric that captures that and most teams don't have the language to even argue for it. The deletion afternoon produced understanding that I'll carry forward for as long as I work on that code, and there's no field in any tracking system where that goes.

Collapse
 
mnemehq profile image
Theo Valmis

Deleting it is usually the fastest way to actually understand a system, because you find out what depends on it in a way reading never surfaces. Curious whether any of those 200 lines turned out to be quietly load-bearing in a way the tests didn't catch.

Collapse
 
gamya_m profile image
Gamya

Yes, actually — and it was exactly the category that tests don't catch well. There was a defensive nil check that looked redundant on paper, handled a condition that seemed impossible given the current flow, and turned out to be protecting against a sequence of user actions that was technically reachable but only under a specific set of steps I'd half-forgotten I'd built. The tests didn't catch it because the tests were written for the happy path and the common edge cases, not for that particular sequence. Deleting the check and then tracing what actually broke was the only way to understand why it was there, which is a much more complete kind of understanding than reading the check and assuming it was probably defensive boilerplate. The ones that turn out to be load-bearing are genuinely the most valuable deletions, even if they end up getting put back.

Collapse
 
forwardava profile image
Oscar Garcia

Hi Gamya.
This really connected with me. I think many developers have experienced this feeling but don't always talk about it. The speed that AI gives us is amazing, but sometimes we forget that understanding the code is still our responsibility.

I especially liked the part about "code arriving without the context." That is so true. When we write code ourselves, every decision has a story behind it. We remember why we made a choice. But with generated code, we can easily become a stranger to our own project.

I also believe deleting code is one of the best ways to learn. It forces us to ask questions: Why does this exist? Do I really need this? What happens if I remove it? Those questions help us become better engineers, not just faster ones.

AI is a powerful tool, but it should help us think deeper, not make us stop thinking. The best developers in the AI era will not be the ones who generate the most code, but the ones who understand, improve, and take ownership of the code they create.

Thanks for sharing this experience. It is a good reminder that sometimes slowing down for a moment helps us move faster in the long run. 👏

Collapse
 
gamya_m profile image
Gamya

"We can easily become a stranger to our own project" is such an accurate way to put it — and the strange part is it happens gradually enough that you don't notice until you open a file and feel that low-level unease that's hard to name. You shipped it, you own it, and somehow it still feels like someone else's work.

The three questions you listed — why does this exist, do I really need this, what happens if I remove it — are exactly the ones that a read alone won't force you to answer, because reading lets you nod along without committing to a position. Deletion makes the question unavoidable in a way that's uncomfortable and useful at the same time.

"The best developers in the AI era will be the ones who understand, improve, and take ownership" — I think that's right, and I'd add that ownership is the part that's hardest to fake. You can generate code that looks owned, but the moment something breaks in an unexpected way, the difference between understanding and having been in the room when it was generated becomes very obvious very fast.

Collapse
 
technogamerz profile image
𝐓𝐡𝐞 𝐋𝐚𝐳𝐲 𝐆𝐢𝐫𝐥

Hey Gamya! Hope you are doing well

This was such a refreshing read. It's funny how we often measure progress by how many lines we write, but sometimes the biggest win is realizing that fewer lines can actually mean better code.

I really liked how you focused on understanding the why before making changes. Deleting code you didn't write takes confidence, but it also takes patience and respect for the person who wrote it. That balance is something many developers learn only after making a few painful mistakes.

It reminded me that good engineering isn't about adding more—it's about making things simpler, clearer, and easier for the next person (or future you) to understand. Thanks for sharing this lesson in such a relatable way. 💙

And one more thing... I have to say this 😄. You haven't been posting your Swift articles lately, and it honestly feels like eating food without salt. But no pressure at all—post whenever you get the time. I'll be looking forward to reading them whenever they arrive!

Collapse
 
gamya_m profile image
Gamya

Hey! Sorry for the late reply, got a bit caught up with things this week! 😊 And thank you so much for this — "making things simpler, clearer, and easier for the next person" is exactly the standard I was trying to hold myself to, and you put it better than I did in the post itself.

Also haha the Swift articles without salt comment 😄 You're absolutely right, I've been on a bit of a general writing detour but the Swift series is coming back very soon — actually already have the next one lined up. Don't worry, the salt is coming! 🌸🚀

Collapse
 
alexshev profile image
Alex Shev

Deleting unfamiliar code is one of the fastest ways to learn the real architecture. You discover which abstractions are load-bearing, which comments are decoration, and which complexity exists only because nobody wanted to touch it. AI-generated code makes that cleanup skill even more important.

Collapse
 
gamya_m profile image
Gamya

"Which complexity exists only because nobody wanted to touch it" is the category I found most interesting honestly — because it's not wrong exactly, it's just accumulated caution. Someone decided at some point that this thing was too risky to simplify, and then the decision got inherited by everyone who came after them without the original context for why it was scary. Deleting it forces you to either validate that the fear was justified or discover that it wasn't, and both outcomes are more useful than the inherited avoidance.

The AI-generated version of this is a bit different because the complexity doesn't come from accumulated caution, it comes from the model being thorough in ways that don't reflect the actual constraints of your specific system. Defensive checks for conditions that can't occur, helper abstractions that exist because they're good practice in general rather than necessary here. That kind of complexity is weirdly harder to delete because it looks responsible, and you have to understand the system well enough to know that the responsibility is misplaced.

Collapse
 
alexshev profile image
Alex Shev

That distinction between inherited caution and generated thoroughness is sharp.

Old complexity often has history behind it, even if the history is missing. AI-generated complexity can look equally responsible while having no local reason to exist. The review question changes from 'is this good practice?' to 'what specific failure in this system is this protecting us from?'

Thread Thread
 
gamya_m profile image
Gamya

"What specific failure in this system is this protecting us from" is the right question and also the harder one — because good practice in general is easy to verify, you just check it against a standard. But whether this defensive check is protecting against a real failure mode in this specific system requires you to actually understand the system, which is exactly the understanding that wasn't transferred when the code was generated.

The review surface looks the same either way. Inherited caution and generated thoroughness both produce code that looks responsible, handles edge cases, adds defensive checks. The difference is that one of them has a reason that lives somewhere in the history, even if you have to go find it, and the other has a reason that lives in the model's training data and may have no relationship to your actual situation at all. That's a genuinely different kind of thing to evaluate and I don't think most review processes are set up to catch it.

Thread Thread
 
alexshev profile image
Alex Shev

That is the review failure in a nutshell. A defensive check can look responsible whether it came from scar tissue or autocomplete. The only way to tell is to ask for the local failure story. If nobody can name the failure, the check may still be useful, but it should not get the same trust as code that was shaped by a real incident.

Thread Thread
 
gamya_m profile image
Gamya

"Ask for the local failure story" is the right test and also the one that most review processes don't have a step for — because the check looks responsible either way, so there's no obvious prompt to ask where it came from. Scar tissue and autocomplete produce code that's visually identical, and the only thing that distinguishes them is the story behind it, which you have to actively ask for rather than read off the diff. The trust differential makes sense too — a check that survived a real incident has been validated by something that actually happened, whereas a check that arrived from thoroughness is a hypothesis about what might happen, and those two things shouldn't carry the same weight even when the code looks the same.

Collapse
 
embernoglow profile image
EmberNoGlow

AI is just a tool; you shouldn't use it as a replacement for yourself. I know that feeling all too well. AI only creates the illusion of fast coding, but it's not. Coding isn't just lines of text. It's the logic of a program. A workflow. If you don't understand how a program works, you're not the author of that code. You're just accepting the role of a copy-paste agent asking an AI to do everything for you (I didn't mean to offend anyone, btw). True programming is converting your thoughts into code. You should control the agent, not let it make decisions for you. Otherwise, how is your code different from something on a GitHub repo from someone else? You'll still have to understand it.

I'm not saying stop using AI. I'm just saying that you should know how a program's main loop works and be able to explain it to an AI. Knowing the syntax isn't as important as design. Decision making is your responsibility. Blindly trusting AI can lead to poor architectural decisions.

Collapse
 
gamya_m profile image
Gamya

"You're not the author of that code, you're just accepting the role of a copy-paste agent" — that line is uncomfortably accurate for what I was doing during that fast-moving stretch, and I think the discomfort I felt opening the file weeks later was exactly that: the gap between being the person who shipped it and being the person who understood it had gotten wider than I'd admitted to myself.

The distinction you're drawing between syntax and design is the one I keep coming back to. Syntax is the part that's most obviously replaceable — the AI is genuinely better at remembering the exact API call than I am, and I'm fine outsourcing that. But design is the part where the decisions compound. A bad architectural call made in minute two of a feature is still there in month six, except now it's load-bearing and surrounded by other things that assumed it was correct. That's not something the AI can take responsibility for because it doesn't live with the consequences the way the codebase does.

"You should control the agent, not let it make decisions for you" is probably the clearest version of the principle I've seen written down. The deletion exercise was basically me discovering, after the fact, which decisions I'd silently let the agent make. Would have been better to catch them in the moment. 🌸

Collapse
 
espoirsamah profile image
HopeGeek

Exactly.

Collapse
 
itskondrat profile image
Mykola Kondratiuk

deletion is actually the best code review. you can't skim what you're removing

Collapse
 
gamya_m profile image
Gamya

That's exactly it — reading lets you skim because your brain fills in what it expects to see, but deletion forces a verdict. You can't half-delete something, you either remove it or you don't, and making that call requires actually understanding what it does. The delete key is the strictest reviewer on the team.

Collapse
 
itskondrat profile image
Mykola Kondratiuk

the "verdict" framing is the right one. most review feedback hedges — "consider extracting," "might be cleaner as" — but delete forces binary commitment. that binary is where understanding actually happens.

Thread Thread
 
gamya_m profile image
Gamya

The hedging in review feedback is so real — and I think it's partly because suggesting a change is low stakes, you're not committing to anything, you're just raising a possibility. Delete is the opposite of that, it's a committed position that you then have to live with. There's no "consider removing" version of the delete key, you either did it or you didn't, and that irreversibility is exactly what forces the understanding that softer feedback lets you skip.

Thread Thread
 
itskondrat profile image
Mykola Kondratiuk

Irreversibility is the mechanism. A suggestion is deniable after the fact — you only raised it as an option. Delete leaves a diff with your name on it. That accountability is what makes it uncomfortable, and also what separates a real review from a consultation.

Thread Thread
 
gamya_m profile image
Gamya

"A suggestion is deniable after the fact, delete leaves a diff with your name on it" — that's the exact distinction between a review and a consultation, and I think a lot of what passes for code review is actually consultation dressed up as review. You raised concerns, you noted possibilities, technically you did the review. But nobody committed to anything, nobody's name is on a position, and the ambiguity stays in the code because the review never forced anyone out of it. Delete can't do that. The diff is the receipt.

Collapse
 
eduzsh profile image
Edu Peralta

This matches something I see constantly running a few coding agents at once. I had an agent clean up what looked like a dead validation branch last month, and that branch was quietly catching a timezone edge case from an old support ticket nobody remembered. The diff summary called it dead code. It wasn't. Your loan metaphor is right, and the interest rate climbs the more agents you run in parallel, because each one hands you a summary of what it did instead of proof of what it touched.

Collapse
 
gamya_m profile image
Gamya

The timezone edge case one is exactly the kind of thing that keeps me up at night honestly — because dead code is such a reasonable thing to clean up, the agent's reasoning was completely sound, and the summary probably looked totally fine. There was no obvious signal that anything was wrong until the specific condition that branch was quietly handling showed up again. That's the worst category of miss because it passes every sanity check you'd normally run.

The parallel agents point is something I hadn't thought about directly but it makes total sense — the interest compounds because each agent's summary is one more layer of abstraction between you and what actually happened. With one agent you're already trusting a diff over a read. With several running at once you're trusting summaries of diffs, and the gap between what got touched and what you understand got touched keeps widening with every layer. The loan metaphor breaks down a little there actually, because at least with a loan you know how much you borrowed. With parallel agents you might not even know what decisions got made on your behalf until something surfaces that doesn't behave the way you expected.

Collapse
 
raffy_d profile image
Raffy

I think we are all guilty of just vibing away at one point or another. That's why it's also important to also zoom out at times and make attempts to re-evaluate your understanding of the code when doing AI assisted coding. Not even the actual code, but just how everything works under the hood.

Plus, don't do AI assisted coding when you're tired lol! Learned that the hard way. Seems you need a break too!

Collapse
 
gamya_m profile image
Gamya

"Don't do AI assisted coding when you're tired" is genuinely underrated advice that nobody puts in the tutorials 😂 Tired-me has a much lower bar for "this looks right" and a much higher tolerance for accepting things without reading them, which is exactly the wrong combination when the tool is producing confident-looking output that might be quietly wrong. The acceptance rate goes up and the scrutiny goes down at exactly the same time. Bad deal.

The "zoom out and re-evaluate how everything works under the hood" point is the one I keep coming back to too — it's easy to stay at the surface level of "does the feature work" and never check whether the way it works makes sense for the system around it. The deletion exercise basically forced me to zoom out because I couldn't delete things confidently without understanding the bigger picture first. Accidental architecture review. 😄🌸

Collapse
 
alexshev profile image
Alex Shev

Deleting generated code is one of the best learning loops because it forces you to identify the actual boundary. Writing can hide uncertainty; deletion exposes it. If you can remove 200 lines and explain why the system got clearer, you probably learned more than if you had accepted another generated patch.

Collapse
 
gamya_m profile image
Gamya

"Writing can hide uncertainty, deletion exposes it" is such a clean way to put the difference — when you're writing or accepting code you can stay in the comfortable zone of "this seems right" without ever having to commit to why. Deletion won't let you do that. Every line you remove is a position you're taking, and every line you decide to keep is also a position, and both require you to actually know what you're talking about in a way that passive acceptance never does.

The "explain why the system got clearer" bar is a good one to hold yourself to as well — if you can't articulate what got better and why, you might have just made it smaller rather than actually cleaner, which are not the same thing. The 200 lines I removed made sense to me precisely because I could say what each category of deletion was doing, and having to find that explanation was most of the learning.

Collapse
 
vivek_404_x profile image
Vivek Patel

Hi everyone,

I'm building a Civic Complaint Management System for my college major project. Since many similar projects already exist, I want to make mine more practical, unique, and closer to a real-world solution.

I'm looking for suggestions on questions like:

What features would make this project stand out?

What real problems do existing civic complaint systems still fail to solve?

What AI features would actually add value?

How can I prevent officers from falsely marking a complaint as "Resolved" without proper verification? (e.g., proof of work, validation, citizen confirmation, etc.)

What security, workflow, or accountability features would you add?

If you were evaluating this as a project, what would impress you?

I'm intentionally not sharing my own ideas because I'd like unbiased suggestions from the community.

Thanks!

Collapse
 
gamya_m profile image
Gamya

The "effective months later" part is really the whole thing isn't it — the cost of not understanding something rarely shows up on the same day you accepted it, which is exactly why it's so easy to keep doing. Everything looks fine, the feature works, you move on, and the understanding debt just sits there quietly accumulating until the moment you need to touch it again and suddenly it's very loud.

Reading generated code like it belongs to someone else is the framing that actually changed how I approach it day to day — because when it's "your" code you have a bias toward defending it, assuming it's right, filling in the gaps with charitable assumptions. When it's a stranger's PR you read it with appropriate suspicion and ask questions you'd never ask about your own work. Same code, completely different posture, and the posture is what decides whether you actually understand it or just accept it. Really glad that part landed!

Collapse
 
voltradoc profile image
Dr Haina

I really appreciate these posts. They give me insights into how developers think and work, and help me understand their perspective much better. 🙌

Collapse
 
gamya_m profile image
Gamya

That honestly means a lot to hear — one of the things I genuinely enjoy about writing these is that they end up reaching people outside the usual developer bubble, and hearing that the perspective comes through clearly is really the best kind of feedback. Thank you for reading! 🌸

 
gamya_m profile image
Gamya

Exactly—same tool, completely different outcome depending on the relationship you have with what it produces. Hope it's useful in practice; I would love to hear how it goes if you try it!