Rules Hub
Coding Rules Library
Rule priority, scope & exceptions
Use this to align rules with the senior-level structure (P0/P1/P2, scope, exceptions/tradeoffs).
Don't use an abstract base class that adds no behavior; extract the real duplication instead
Only introduce a base class when it factors out genuinely shared behavior. If all it provides is a flag or a stateless method, make that a plain utility function and put the actually-duplicated logic (e.g. a try/catch+fallback) in the shared layer.
Bad example
| 1 | abstract class MarketDataBaseProvider { |
| 2 | protected shouldUseMda(): boolean { |
| 3 | return featureEnabled('mda'); |
| 4 | } |
| 5 | } |
| 6 |
|
| 7 | class StockProvider extends MarketDataBaseProvider { |
| 8 | async fetch() { |
| 9 | try { |
| 10 | return this.shouldUseMda() ? this.fromMda() : this.fromLegacy(); |
| 11 | } catch { |
| 12 | return this.fromLegacy(); |
| 13 | } |
| 14 | } |
| 15 | } |
Explanation (EN)
The base class only wraps a stateless predicate, so inheritance buys nothing — meanwhile the try/catch+fallback that is genuinely repeated in every child is left duplicated.
Objašnjenje (HR)
Bazna klasa samo omata predikat bez stanja, pa nasljeđivanje ne donosi ništa — dok try/catch+fallback koji se stvarno ponavlja u svakom djetetu ostaje dupliciran.
Good example
| 1 | const shouldUseMda = () => featureEnabled('mda'); |
| 2 |
|
| 3 | async function withLegacyFallback<T>(primary: () => Promise<T>, legacy: () => Promise<T>) { |
| 4 | try { |
| 5 | return await primary(); |
| 6 | } catch { |
| 7 | return await legacy(); |
| 8 | } |
| 9 | } |
| 10 |
|
| 11 | class StockProvider { |
| 12 | async fetch() { |
| 13 | return withLegacyFallback( |
| 14 | () => (shouldUseMda() ? this.fromMda() : this.fromLegacy()), |
| 15 | () => this.fromLegacy(), |
| 16 | ); |
| 17 | } |
| 18 | } |
Explanation (EN)
The stateless check becomes a plain function and the truly repeated try/catch+fallback is hoisted into a reusable helper, removing the inheritance ceremony while actually killing duplication.
Objašnjenje (HR)
Provjera bez stanja postaje obična funkcija, a stvarno ponavljani try/catch+fallback diže se u višekratni helper, uklanjajući ceremoniju nasljeđivanja i istovremeno stvarno eliminirajući dupliciranje.
Notes (EN)
Ask: what behavior or state does the base class hold? If the answer is none, prefer composition/utilities over inheritance.
Bilješke (HR)
Pitajte se: kakvo ponašanje ili stanje drži bazna klasa? Ako je odgovor nikakvo, preferirajte kompoziciju/utilityje umjesto nasljeđivanja.
Exceptions / Tradeoffs (EN)
A base class is justified when it carries shared state, template-method workflow, or a polymorphic contract consumed elsewhere.
Iznimke / Tradeoffi (HR)
Bazna klasa je opravdana kad nosi dijeljeno stanje, template-method tijek ili polimorfni ugovor koji se koristi drugdje.