8 ms·
Clean code in the age of coding agents
- Insensitivity 6mo ago> LLMs are pretty good at picking up the style in your repo. So keeping it clean and organized already helps. At least in my experience, they are good at imitating a "visually" similar style, but they'll hide a lot of coupling that is easy to miss, since they don't understand the concepts they're imitating. They think "Clean Code" means splitting into tiny functions, rather than cohesive functions. The Uncle Bob style of "Clean Code" is horrifying They're also very trigger-happy to add methods to interfaces (or contracts), that leak implementation detail, or for testing, which means they are testing implementation rather than behavior
- datadrivenangel 6mo agoAnd so many helper factories in places that don't need it.
- heyethan 6mo ago[dead]
- crabbone 6mo agoYeah. We have an AI reviewer. Just now I had a PR where I didn't normalize paths in some configuration and then compared them. I.e. let's say the configuration had file = /foo/bar/ and then my code would do: if file == other_file: ... instead of: if normalized(file) == normalized(other_file): ... and the AI reviewer suggested a fix by removing the trailing slash instead. I.e. the fix would have "worked", but it would've been a bad fix because the configuration isn't under program's control and can't ensure paths are normalized. I've encountered a lot of attempted "fixes" of this kind. So, in my experience, it's good to have AI look at the PR because it's just another free pair of eyes, but it's not very good at fixing the problems when it does find them. Also, it tends to miss conceptual problems, concentrating on borderline irrelevant issues (eg. an error raised by the code in the use-case beyond the scope of the program like when a CI script doesn't address the case when Git is not installed, which is of no real merit).
- fooker 6mo agoI’ll go against the prevailing wisdom and bet that clean code does not matter any more. No more than the exact order of items being placed in main memory matters now. This used to be a pretty significant consideration in software engineering until the early 1990s. This is almost completely irrelevant when we have ‘unlimited’ memory. Similarly generating code, refactoring, implementing large changes are easy to a point now that you can just rewrite stuff later. If you are not happy about how something is designed, a two sentence prompt fixes it in a million line codebase in thirty minutes.
- iterateoften 6mo agoGarbage in, garbage out. The llm is forced to eat its own output. If the output is garbage, its inputs will be garbage in future passes. How code is structured makes the llm implement new features in different ways.
- aspenmartin 6mo agoWhy would “messy” code be garbage? Also LLMs do a great job even today at assessing what code is trying to do and/or asking you for more context. I think the article is well balanced though: it’s probably worth it for the next few months to try to help the agent out a bit with code quality and high level guidance on coding practices. But as OP says this is clearly temporary.
- iterateoften 6mo agoThe definitions of what is messy or clean will change will llms… But there will always be a spectrum of structures that are better for the llm to code with, and coding with less optimal patterns will have negative feedback effects as the loop goes on.
- aspenmartin 6mo agoI agree with you but you can dedicate tokens to fixing the bad code that agents do today. I don’t disagree with anything you’re saying. I think the practical implication is instead of pain and jira we’ll just have dedicated audit and refactor token budgets.
- jake-coworker 6mo agoI generally agree - I think anyone who's seriously experimented with agentic coding has encountered both: 1 - Surprising success when an agent can build on top of established patterns & abstractions 2 - A deep hole of "make it work" when an LLM digs a whole it can't get out of, and fails to anticipate edge cases/discover hidden behavior. The same things that make it easier for humans to contribute code make it easier for LLMs to contribute code.
- bmitc 6mo agoBasically, nothing has changed except the increase in noise. So all the suits who refuse to understand what software is have yet again decided to make things worse for professionals and for people who actually know what they're doing.
- Teever 6mo agoCan you expand on what you think software is in this context? Why do you think taht the suits refuse to understand what it is?
- rvz 6mo agoTo the "suits" AI means "efficiency". Efficiency also means to them, "less costs" and when they talk about "costs" they mean "headcount" which that is employees. Put it together and the suits want to reduce headcount using AI. To them, "clean code" is a scam and a waste of time that doesn't yield them quick returns, but a weak reason for software engineers to justifying their roles.
- bmitc 6mo agoAlthough I appreciate the question, I don't necessarily want to get into the discussion about what software is here. This is mainly due to the lack of time I currently have. As an elaboration, software seems to be this thing that everyone thinks they know about. This is extremely common in my experience in that scientists (particularly physicists and engineering-based scientists), hardware engineers, "the suits", as it were, and more think that code is just this dumb, simple thing that you slap on top of things like algorithms, hardware, feature requests, etc. They continually fail to realize that the overhead and interruptions that happen when software is onboarded is entirely because it is software engineers that understand how to design and think about systems and that it's the software engineers who end up discovering that no one has done requirements gathering, performed holistic systems thinking, understood edge cases, thought about user workflows, etc. This is even though that they think this work is already "done" and "just needs software" now. I'm still struggling to understand why this is the case for software. So my point was that everyone who isn't a software engineer has this opinion that it's going to automate the software engineer away, and yet, they have failed to _actually ask the professionals in this domain_ as to what they think. The decision has been made because, again, software is just this thing that is the last mile for things that have already been thought about and are "done". They think that automating software will get rid of the pesky software engineer who gets in the way between the thing that's "done" and this last mile being completed.
- BeetleB 6mo agoEver since AI coding became a thing, Clean Code advocates have been trying to get LLMs to conform. I was hoping this submission would declare "Success!" and show how he did it, but sadly it's devoid of anything actionable. I'm not a fan of Clean Code[1], but the only tip I can give is: Don't instruct the LLM to write code in the form of Clean Code by Robert Martin. Itemize all the things you view as clean code, and put that in CLAUDE.md or wherever. You'll get better luck that way. [1] I'm also not that anti-Uncle-Bob as some are.
- ambewas 6mo agoin my experience, as long as you set up a decent set of agent definitions & a good skillset, and work in an already pretty clean codebase with established standards, the code quality an agent outputs is actually really good. Couple that with a self-correcting loop (design->code->PR review->QA review in playwright MCP->back to code etc), orchestrated by a swarm coordinator agent, and the quality increases even further.
- CharlieDigital 6mo agoI have a bit more practical approach here (write up at some point): the most important thing is to rethink how you are instructing the agents and do not only rely on your existing codebase because: 1) you may have some legacy practices, 2) it is a reflection of many hands, 3) it becomes very random based on what files the agent picks up. Instead, you should approach it as if instructing the agent to write "perfect" code (whatever that means in the context of your patterns and practices, language, etc.). How should exceptions be handled? How should parameters be named? How should telemetry and logging be added? How should new modules to be added? What are the exact steps? Do not let the agent randomly pick from your existing codebase unless it is already highly consistent; tell it exactly what "perfect" looks like.
- nlh 6mo agoI'm guessing a lot of similar debates were had in the 1970s when we first started compiling C to Assembly, and I wonder if the outcome will be the same. (BTW: I was not around then, so if I'm guessing wrong here please correct me!) Over time compilers have gotten better and we're now at the point where we trust them enough that we don't need to review the Assembly or machine code for cleanliness, optimization, etc. And in fact we've even moved at least one abstraction layer up. Are there mission-critical inner loops in systems these days that DO need hand-written C or Assembly? Sure. Does that matter for 99% of software projects? Negative. I'm extrapolating that AI-generated code will follow the same path.
- groos 6mo agoThe high level language -> assembly seems like an apt analogy for using LLMs but I would like to argue that it is only a weak one. The reason in that, previously, both the high level language and the assembly language had well defined semantics and the transform was deterministic whereas now you are using English or other human language, with ambiguities and lacking well-defined semantics. The reason math symbolisms were invented in the first place is because human language did not have the required unambiguous precision, and if we encounter hurdles with LLMs, we may need to reinvent this once more.
- crabbone 6mo agoThis debate didn't start with C. Compilers existed pretty much since Algol, and the debate was there too. There were also similar arguments made about certain other mechanisms that programmers expected to become obsolete over time, but which never did. For example, all the "visual programming" that later transformed into "no code" programming never really delivered on the promise. (We still have a bunch of tools and languages that carry the "visual" adjective in them, but very few people remember why that adjective was there in the first place). Eg. Visual Basic was supposed to be visual because you'd build interfaces by dragging components into the designer view of your IDE and then using graphical editor style interface to position them, label them etc. Not only MSVS came with the "designer" view, Eclipse had one too, probably more than one. Swing had its own "designer" as did a bunch of other UI frameworks. Programmers also believed that flat files will be gone and be replaced by some sort of a structured database with an advanced query mechanism. Again, never happened, and the idea is mostly abandoned today as the storage moved on in a completely different direction. Object databases: (not to be confused with object store like S3) the direction that seemed all but assured at the down of objects -- never really happened either. Instead we still struggle with ORM, and it's not sure that ORM will not die off eventually. I wouldn't be so quick as to profess AI prompt libraries to replace source code. There are many problems with this idea, beside the quality of the output. The control of the AI agents and their longevity (in source code this is ensured by standards). Collaborative programming: how people w/o access to the agent are supposed to work with the prompt library instead of the source code? I don't think we are anywhere close to the point where we can realistically expect prompt libraries to be a generally accepted replacement for the source code. In some niche cases? -- perhaps, but not more than that.
- j45 6mo agoIt's important to remember humans have shipped slop too, and code that isn't clean. When the training is across code with varying styles, it is going to take effort to get this technology performing in a standardized way, especially when what's possible changes every 3 months.
- gz09 6mo agoour CLAUDE.md / AGENTS.md specifically calls out good engineering books, which I think does help: From https://github.com/feldera/feldera/blob/main/CLAUDE.md https://github.com/feldera/feldera/blob/main/CLAUDE.md: - Adhere to rules in "Code Complete" by Steve McConnell. - Adhere to rules in "The Art of Readable Code" by Dustin Boswell & Trevor Foucher. - Adhere to rules in "Bugs in Writing: A Guide to Debugging Your Prose" by Lyn Dupre. - Adhere to rules in "The Elements of Style, Fourth Edition" by William Strunk Jr. & E. B. White e.g., mentioning Elements of Style and Bugs in Writing certainly has helped our review LLM to make some great suggestions about English documentation PRs in the past.
- palmotea 6mo ago> - Adhere to rules in "The Elements of Style, Fourth Edition" by William Strunk Jr. & E. B. White FYI: The third edition was the last one by E. B. White. The fourth edition was revised by someone whose identity is unclear. For something so opinionated, I'd like to know whose opinions I'm reading. Not that it really matters for your LLM prompt, but it's worth pointing out.
- esilverberg2 6mo agoOur company makes extensive use of architectural linters -- Konsist for Android and Harmonize for Swift. At this point we have hundreds of architectural lint rules that the AI will violate, read, and correct. We also describe our architecture in a variety of skills files. I can't imagine relying solely on markdown files to keep consistency throughout our codebase, the AI still makes too many mistakes or shortcuts.
- johntash 6mo agoDo you have any recommendations of something like konsist/harmonize that work for multiple languages by any chance? I've been looking for a solution to org-wide architectural linting that can has custom rules or something so I could add rules for a Dockerfile as well as python in the same way.
- deleted 6mo ago[deleted]
- embedding-shape 6mo ago4. Iterate on a AGENTS.md (or any other similar file you can reuse) that you keep updating every time the agent does something wrong. Don't make an LLM write this file, write it with your own words. Iterate on it whenever the agent did something wrong, then retry the same prompt to verify it actually steers the agent correctly. Eventually you'll build up a relatively concise file with your personal "coding guidelines" that the agent can stick with with relative ease.
- zamalek 6mo agoThe last two weeks with Claude has been a nightmare with code quality, it outright ignores standards (in CLAUDE.md). Just yesterday I was reviewed a PR from a coworker where it undid some compliant code, and then proceeded to struggle with exactly what the standards were designed to address. I threw in the towel last night and switched to codex, which has actually been following instructions.
- Verdex 6mo agoHeres the thing about clean code. Is it really good? Or is it just something that people get familiar with and actually familiarity is all that matters. You can't really run the experiment because to do it you have to isolate a bunch of software engineers and carefully measure them as they go through parallel test careers. I mean I guess you could measure it but it's expensive and time consuming and likely to have massive experimental issues. Although now you can sort of run the experiment with an LLM. Clean code vs unclean code. Let's redefine clean code to mean this other thing. Rerun everything from a blank state and then give it identical inputs. Evaluate on tokens used, time spent, propensity for unit tests to fail, and rework. The history of science and technology is people coming up with simple but wrong untestable theories which topple over once someone invents a thingamajig that allows tests to be run.
- tracker1 6mo agoI'm with you... personally, I always found Clean/Onion to be more annoying than helpful in practice... you're working on a feature or section of an application, only now you have to work across disconnected, mirrored trees of structure in order to work on a given feature. I tend to prefer Feature-Centric Layout or even Vertical Slices, where related work is closer together based on what is being worked on as opposed to the type of work being done. I find it to be far more discoverable in practice while able to be simpler and easier to maintain over time... no need to add unnecessary complexity at all. In general, you don't need a lot of the patterns introduced by Clean or Onion structures as you aren't creating multiple, in production, implementations of interfaces and you don't need that type of inheritance for testing. Just my own take... which of course, has been fighting upstream having done a lot of work in the .Net space.
- dirtikiti 6mo agoApplause. I am in .net as well. The clean code virus runs rampant. Swimming in DTOs and ViewModels that are exact copies of Models; services that have two methods in them: a command method and then the actual command the command method calls, when the calling class already has access to the data the command method is executing; 3 layers of generic abstractions that ultimately boil down to a 3 method class. Debugging anything is a nightmare with all the jumps through all the different classes. Hell, just learning the code base was a nightmare. Now I'm balls deep in a warehouse migration, which means rewriting the ETL to accommodate both systems until we flip the switch. And the people who originally wrote the ETL apparently didn't read the documentation for any of it.
- jbergqvist 6mo agoIn my experience, one reason for unnecessarily complex solutions during vibe coding is the incremental work pattern. Most users don't spend much time designing the solution, but instead jump quickly to implementation and then iterate. When doing that, the models seem prone to applying more short-sighted patches to existing code instead of doing a larger refactor that would simplify it all. Other than spending more time on design, I also usually ask the agent to spawn a few subagents to review an implementation from different perspectives like readability, simplicity, maintainability, modularity etc, then aggregate and analyze their proposals and prioritize. It's not a silver bullet and many times there are no objective right answers, but it works surprisingly well.
- perrygeo 6mo agoThis article is more about how to get LLMs to adhere to existing definitions. I was hoping this would explore some re-definitions of "clean code". DRY is a principle that comes up frequently. But is repetition really that bad when LLMs can trivially edit all instances of the pattern and keep them in sync? LLMs, by contrast, cannot understand a leaky abstraction - the typical result when you hastily apply DRY. So "clean code" in a era of LLMs might be mean more explicit and repetitive, less abstract.
- standardly 6mo agoIME the best approach is keeping prompts as narrow and constrained as possible. its better to structure your own methods etc in a DD, and have implementation details fleshed out under supervision, rather than letting the agent create 30 new methods with unwanted/unused/unprompted arguments. once it starts thinking for more than a minute, i'm already a bit worried, and having it suggest or commit more than 100 lines at a time almost always ends up with more than what I asked for. im sure folks are using it for much larger tasks than what im referring to but this is my experience with small projects