Any issues with your code that could hinder future development or maintenance (e.g., fixing bugs, changing behaviour, adding new features). Such issues could include poor documentation, poor code organisation, poor identifiers, poorly considered data structures, lack of test coverage and magic numbers. Code maintenance issues pertain to code readability and modularity—they have nothing to do with runtime performance.

Documentation

If subprograms lack header documentation and in-line comments explaining program logic, they could be poorly documented. Likewise, excessive commenting is an example of poor documentation as it hinders readability: good documentation should assume competence with programming but unfamiliarity with the codebase.

Code organisation

Poor code organisation generally takes two forms: lack of modularity and poor organisation on the page.

Lack of modularity

Code that is not DRY and does not use reusable elements (e.g., subprograms, classes) lacks modularity. Similarly, code where the ‘reusable’ elements are tightly coupled with program logic may also lack modularity. Some level of coupling with program logic is necessary, but well-designed, modular code, uses abstraction when possible. Too much abstraction can, however, be a barrier to readability!

Organisation on the page

If there are thousands of lines of code on one page, this is a sign it could be poorly organised. Well-organised code separates its concerns across multiple files, potentially using custom libraries to group similar domains together.

Within files, if related code blocks are disparate, this could be an example of poor organisation of code on the page.

Global variables

Global variables are generally poor practice as they make code less modular and potentially less readable by polluting the namespace.

Magic numbers

Hard-coded values make it hard to maintain code as they make code less readable and potentially less modular.

Identifiers

Meaningful identifiers reduce the need for additional documentation by making code self-documenting. Meaningless identifiers make code harder to read. Similarly, gratuitously long identifiers indicate lack of insight and make code harder to read, e.g., variable_for_iterating_through_a_python_list_to_find_a_search_query_from_a_linear_search_as_specified_by_the_user is grotesque.

Data structures

Using appropriate data structures makes code easier to maintain and extend. Poor use of data structures makes code frustrating to work with.

For example, when storing several names, a non-thinking approach would be to store each name in a separate variable: name1, name2, name3. A more thoughtful approach would use a data structure like a list: names = [].

Sensible use of data structures

If you ever find yourself creating several variables with different number suffixes, stop. Think about data structures that would be more appropriate. Perhaps a list.

Poorly chosen data structures can also pose problems. Creating a custom object with data hiding and hundreds of methods to store three names is almost certainly a bad idea.

Test coverage

One of the main benefits of comprehensive test coverage is peace of mind that code still works when it is changed. The changes are fine if the tests pass and the comprehensive test coverage gives confidence that the program works as expected.

When test coverage isn’t comprehensive, maintenance becomes harder as either testing needs to be done ad-hoc or testing is skipped and issues go undetected. Taking the time to write comprehensive tests early in development makes future maintenance much easier.