A DRY tool, or the only copy/paste allowed

← A DRY tool, or the only copy/paste allowed

Ashterix

docs: English JsonRpcBundle docs, REST and MCP Bundle sections, package cards

en/articles/from-tg/php/traits.md new file +49 −0

A DRY tool, or the only copy/paste allowed

Many technical interviews show that developers either don't use traits at all or use them without understanding their practical purpose. Often they are simply afraid of them — supposedly a trait's behavior can be non-obvious or cause bugs that are hard to track down.

But the point is that traits are just a tool that should be used where it brings the most benefit.

And most often these are not service classes (although there are situations where traits fit there too), but object structures: entities, DTOs, VOs, events and so on. That's exactly where traits are the ideal tool for handing out typical behavior without an unnecessary hierarchy and without violating DRY.

A good example is the knplabs/doctrine-behaviors library. Through its interfaces it gives your entities typical behavior (for example, Timestampable, SoftDeletable, Translatable and so on), but instead of implementing the interfaces in every class, you just add the implementation by including the corresponding trait. No crutches in the form of abstract parent classes that "provide behavior" — only clean code, a concise use TraitName and no copy/paste.

Although, to be fair, copy/paste still happens — just not in the code, but at the interpreter level.

Conclusion

Traits are a way to stick to DRY and avoid inheritance where it doesn't belong. It's a very powerful tool if you use it correctly and where it's needed.

Tip: how do you make a trait guaranteed to work in any class without errors?

Never access properties or methods of the class the trait is included into from inside the trait. A trait doesn't know where it will be inserted — so the context of any trait must be self-contained. This is the main condition for your trait to work reliably and not cause errors.

Bad:

trait BadTrait {
    public function generateUniqueId(): void 
    {
        $this->id = uniqid();
    }
}

Good:

trait GoodTrait {
    protected string $id; 

    public function generateUniqueId(): void 
    {
        $this->id = uniqid();
    }
}

First published in Ukrainian in the Telegram channel “Created to die” on June 26, 2025.