Teaching Clean Code: what actually sticks
At EPAM I was on the Mentoring Committee, and a large part of that was teaching Clean Code and Clean Design. I did it in three ways: live code walkthroughs, one-to-one mentoring with newer developers, and day-to-day code reviews.
We covered the usual ground: naming and readability, SOLID and design principles, refactoring legacy code safely, and testing. The topics weren’t the hard part. Almost every engineer has heard “use meaningful names” and “keep functions small”. The hard part is getting people to actually write that way under real conditions. This is what I found worked.
Use their code, not slides
Theory slides about clean code don’t change how people write. Refactoring their own code does.
When a mentee watched us take a method they had written and improve it step by step, the lesson stuck in a way no example from a book could match. They knew the code, they knew why it had ended up that way, and they could see exactly what was different afterwards.
Show before and after
Next to their own code, the most effective tool was a simple before-and-after: the same logic written messily and then cleanly, side by side. Here’s the kind of example I mean:
// Before
public double calc(List<Order> l, boolean f) {
double t = 0;
for (Order o : l) {
if (f && o.getStatus().equals("C")) continue;
t += o.getAmt() * o.getQty();
}
return t;
}
// After
public double totalValue(List<Order> orders, boolean excludeCancelled) {
return orders.stream()
.filter(order -> !(excludeCancelled && order.isCancelled()))
.mapToDouble(Order::lineTotal)
.sum();
}
Nobody needs a lecture to see which one they’d rather maintain. The comparison does the arguing for you.
Explain the why, not just the rule
Rules without reasons don’t survive the first deadline. “Single responsibility” sounds like an abstract principle until you show what happens when a class with four responsibilities needs one of them changed: you touch everything, test everything, and still break something.
Once people understood why a principle existed, they could apply it with judgement instead of following a checklist.
Repeat it in every review
Workshops start the habit, but code reviews are where it forms. The same few points came up again and again across many pull requests: a vague name here, a method doing too much there, a missing test. Repetition sounds dull, but it’s what turns a lesson into a reflex.
The hard parts
Review comments can feel personal. A comment on someone’s code can easily read as a comment on them, especially for someone new. What helps is keeping every comment about the code and the reason behind it, never about the person who wrote it.
Deadlines always push “clean later”. “We’ll clean it up later” usually means never. The answer isn’t to fight every deadline. It’s to make clean the default way of working, so it doesn’t cost extra time.
“This codebase is already a mess, so why bother?” Legacy code makes clean code feel pointless. The way through is small, safe improvements: leave each file a little better than you found it, with tests protecting the change. You don’t need permission for a big rewrite to start.
What changed
I’ll be honest that this is hard to measure. There’s no metric for “writes cleaner code”. But the changes I saw were real:
- First drafts got cleaner, and pull requests needed fewer rounds of review.
- Tests became a normal part of people’s work, not an afterthought.
- Some of the people I mentored started giving clean-code feedback in their own reviews.
That last one is the result I value most. Teaching clean code properly means the habit spreads beyond you.