Our industry turned "write readable code" into a religion. And like most religions, the followers forgot the original message.
A teammate picked up a ticket — add a discount field to checkout. One day of work, two with tests.
He came back three days later with a PR that touched 40 files. He'd refactored the payment module into a strategy pattern. Created interfaces. Added a factory because "what if we need more providers later?"
What the ticket needed:
checkout.tsx — add input fieldapi/checkout.ts — add discount paramdb migration — add columnWhat was delivered:
DiscountStrategy interfacePercentageDiscountStrategyFixedAmountDiscountStrategyDiscountStrategyFactoryDiscountValidatorServiceEstimate: 1-2 days. Actual: 5 days.
We had two providers. We'd had two for three years.
He genuinely thought he was doing the right thing. Meanwhile, PM was asking why a simple field took half a sprint.
Uncle Bob's book has good advice. Functions should be small. Names should be meaningful. Fine.
But we turned guidelines into commandments. We stopped asking "does this help?" and started asking "does this follow the rules?"
A UserNameValidator, UserEmailValidator, UserAgeValidator, and a UserValidatorFactory that creates a CompositeUserValidator. To validate three fields.
Someone will say this violates Single Responsibility Principle because it checks three fields, therefore "three things."
Checking user input IS one thing. The mental gymnastics to argue otherwise is the whole problem.
Extract function — you gain reusability, you lose locality.
Create interface — you gain flexibility, you lose directness.
Apply pattern — you gain a "known solution," you lose simplicity.
Add abstraction — you gain future-proofing, you lose present clarity.
None of these are free. The clean code crowd pretends they are.
Before refactoring anything:
"Is this code causing problems RIGHT NOW?"
Not theoretically. Not in some hypothetical future. Right now.
Refactor if:
Don't refactor if:
If no problems right now — leave it alone and go build something.
calculateTotalPrice > calc. Always worth it.Junior me thought clean code = good engineer. Mid-level me thought design patterns = good engineer. Now I think delivering working software that people can maintain = good engineer.
Stop performing on pull requests. Start shipping.
"Good enough" code that ships beats "perfect" code that doesn't. Every time.
— blanho
Stop chasing job titles. Start chasing knowledge. The title doesn't make you competent.
5 years of experience, 200 applications, 3 callbacks. Something is very wrong.
You don't have Netflix's problems. You have 3 developers and a Postgres database.
// "Clean" code (129 lines across 5 files)
interface Validator<T> {
validate(value: T): ValidationResult;
}
class UserNameValidator implements Validator<string> { ... }
class UserEmailValidator implements Validator<string> { ... }
class UserAgeValidator implements Validator<number> { ... }
class CompositeUserValidator implements Validator<User> { ... }
class UserValidatorFactory { ... }
// What was actually needed (8 lines, 1 file)
function validateUser(user: User) {
const errors = []
if (!user.name) errors.push('Name is required')
if (!user.email.includes('@')) errors.push('Invalid email')
if (user.age < 18) errors.push('Must be 18+')
return errors
}