McWafflestix has a lot of good points at the organizational level. A dysfunctional software engineering organization will make it nearly impossible to produce good code, if only for simple pragmatic reasons such as lacking decent tools and automation.
Even with a functional organization and good tools, however, anti-patterns can grow on you. Some characteristics I look for are below, with the rules of thumb I use in parentheses.
Some signs:
- In OOP, extremely deep object hierarchies (more than 3 levels)
- Copy-and-paste code that appears multiple times (more than 3) OR code in multiple locations that performs substantially the same function
- Copy-and-paste code with minor/subtle changes that cannot be easily/sensibly parameterized
I also tend to look for areas in the code which are not intuitive, readable, (insert favorite adjective here). This speaks less to code quality and more to your team's ability to work together effectively. Over time, a team's codebase usually picks up a dialect - common semantics and vocabulary for method and variable names, informal conventions about ordering methods or operations, etc.
Ensuring the consistency of that dialect can help significantly, especially when one team member has to work on a segment of code that had, until that moment, been maintained by another person. When code begins drifting substantially enough off the common dialect, that can be a sign that a design anti-pattern is emerging.