- code that is inconsistent. This causes additional overhead and mental burden when thinking about code. Trying to find what's truly logically different is hard when there are additional differences that shouldn't be there in the first place.
- incomplete code. This might be a concept that was done halfway and then stopped. So there's additional mental burden of, this is what is meant to be done, but it hasn't been done in these places yet, so understand the code is being pulled in two different directions, and also keep that original plan in mind so that you converge those past plans with your current line of work.
- buggy code. Code that looks like it should work but then doesn't causes issues when you can't tell if an error is in something new you're developing or something that existed already.
- code that is hard to accommodate the new change. This one finally gets a bit fuzzier, as the more infinitely flexible you make it, the worse it'll be at matching the task at hand. If you are better at predicting the future, you might be able to set yourself up so that it's slightly easier for anticipated future development, with minimal impact to what you currently need it to accomplish. So this might not be an issue of "unclean" code but just the reality of changing businesses. Of course, it is also possible to just make design decisions here that are objectively bad no matter what the future plans are.
You'll kind of find what is considered "clean" depending on your work place, what your engineering culture is like, what tradeoffs and priorities you have as an org. Basically, what is it that makes my job harder? That's what you then need to "clean" up. e.g. some orgs might say that lack of unit tests makes their job harder, and others might say it makes little difference, so then lack of testing might or might be a "clean" point depending on your org.