Magento 2 - good practice to use/avoid magic getters?

The above question is about using magic methods vs. getDataByKey or getDataByPath. I think there is a third option, too, and that is implementing real getter and setter methods.

The getData* methods all have the downside that they have to be annotated for type inference to work.
Usually that is done with a /* @var string $foo */ annotation above the getData* call.
This is a bit smelly, because the type of the data should be declared in the class that contains the data, not the class that calls getData*.
The reason for that is that if the data changes, the class is the most likely to be updated, not all getData* call sites.
That is why I think real methods increase maintainability compared to using getData* accessors.

So I think it boils down to a trade off between maintainability and quicker implementation (less code to write).

Luckily, nowadays IDEs are really good at creating the getter and setter implementations for us, so that argument doesn't really apply any more.

One more argument against magic getters and setters that is missing from the question above is that it's not possible to create plugins for them.

The only other value I think I can add to the topic is trying to collect the reasons to use or not to use @method annotations, if implementing real methods is out of the question for some reason.

Pros

  • A @method annotation is a little less code to write compared to implementing a real getter and setter. This is barely true though nowadays because IDEs are good at generating accessor methods, so that's not a real benefit any more.

Cons

  • It's easy for things to go wrong.
    • Annotations are comments, they become obsolete easily when code evolves but the annotations are not updated. Real methods are more robust.
    • It's possible to add multiple annotations with different type signatures without an interpreter error - static code analysis behavior is undefined, and it can lead to subtle bugs that are hard to track down.
    • If both a @method annotation and a real method with the same name exist, the annotation type signature overrides the real method during static code analysis, which is the opposite of what the PHP interpreter does. This again can easily lead to subtle bugs.

For the above reasons I personally do not use @method annotations if I can avoid them.
For code that is intended to live long I implement real getter and setter methods. The maintainability gain is worth the effort of triggering the IDE to generate them.

For more experimental code during a spike, or for a simple implementation detail of a module I use getData* methods, too, because I'm lazy.