Dissecting the question, we have two operative terms here: “readable” and “maintainable.”
Let’s talk about what these mean.
“Readable” would describe code that is understood without much inspection or explanation by another developer. You have to choose what parameters you want to describe the “other developer” to know what would be readable to them.
Some things are somewhat universal and limited by human factors. For example, few people can follow poorly named variables. (There’s your first heuristic – are there clear names for variables, classes, methods, and other references?)
Other things are a bit more nuanced. For example, if the developer uses the language you are writing in on a regular basis.
Or, is the developer familiar with the domain the project operates within? How experienced are they as a developer? Do they have a particular background that might make the code more or less readable to them?
But what if you don’t know who the other developer is?
Some simple, practical heuristics to follow:
- Use descriptive variable names. Longer variables are more easily read.
- Use whitespace! Compilers and minifiers mean that whitespace is free. Take advantage of this.
- Practice finding a balance between abstraction and practicality. You do not need 10 layers of redirection for simple tasks; start with the simplest approach and abstract during a refactoring process.
- “Make it work, make it right, make it fast.” In that order. This will immensely help the readability of your code, because you start first from comprehension, and then move towards performance. This establishes your pattern and semantics up front, and you are more likely to maintain good semantics this way.
- Know your audience. If your audience isn’t used to inline lambda calculations, skip it. Even if you believe it is the “best way” to solve the problem, there are other equally good ways most likely available.
- Follow well-established refactoring and OO patterns. Seriously. These things have been tried and tested, and they work. Look here for a good description of many of these patterns: sourcemaking.com/
- Don’t follow rules blindly. Take some time away from the code and revisit it – what sticks out as weird? What sticks out as confusing? When does your brain feel tired?
- Readable code isn’t always maintainable, and vice versa. Maintainable code is established by following good practices and principles.
- Tests are HUGELY important to codebase maintenance. Having good test coverage keeps you from having to load all of the codebases into your working memory (in your head), and reduces errors. (Note: tests won’t eliminate errors altogether; they are there to help you, not to make you obsolete.)
All-in-all, coding is a human process. Follow Hemingway’s advice when writing code. Simpler is typically better.