Software development is modeling
Every aspect we want to see in the result must have corresponding representation. So if two things are related in the domain model, you have to define that relation explicitly or implicitly. For example, in array data structures, the order could be implicitly derived from a position in an array. If you want to introduce the order of items in the hash-map, you need to introduce something that describes it (or switch to an array).
Choosing the wrong building blocks (language, library, framework) will result in more code to express desired behavior. Some tools might not be able to model the desired behavior or future desired behavior. Pulling new messages for the chat via REST endpoint might not be the best tool. You might consider something that pushes new values to the client.
Often we write code on top of an already existing one. Then to be effective, the developer must understand what model is in from of them, Simpler the model easier is to understand it. Fewer features result in a smaller model. And smaller is always better because you need to understand less.
More structure you have in existing code harder will be to introduce new features. Sometimes new features are so alien to the existing model that there is no way around than to rewrite. OOP inheritance is a good example of a structure that might fail in the future.
Code that does not contribute to the desired model might be a source of unnecessary complexity.
Ideally, the tools you choose can describe a model from your domain directly. I think that's thinking those who create DSL to describe the desired model. In practice, you will have to make your abstractions and layers — the only question how many.
Use alternative representations of the model. Visualize your state machines, database relations. Use flame graphs to understand performance bottlenecks. Use paper to make drafts application entities and relations, and data flows, etc.
One abstraction to cover multiple cases is better than many. Better to create all react components as classes than have a mix of functional and class components. Better use RXJS for all data passing that use mix promises and RXJS observables. But be careful. One abstraction with tonnes of if and intertwingled logic is not a thing you're looking for.
Some components happen to look similar in the design but might require different models, such as index and subject pages on this website.
Not all models can grow naturally. It would be best if you consciously designed a model not to cut out viable options. Or optimize for a change.
Do you have more ideas? Let's discuss in the comments