


Precise naming, early returns, short functions, daily refactor, pair programming and project-based evaluation: what nine months training 18 learners taught me about my own code.
Nine months teaching fullstack development to career-switchers. What I learned about clean code, technical debt, and real pedagogy.
From July 2024 to March 2025, I trained a cohort of 18 learners at IAFP La Pyramide (a state-accredited modular training institute in Togo). Program: algorithmic fundamentals, JavaScript, TypeScript, Dart, Next.js, Flutter, React Native, Git. Goal: that they ship autonomous fullstack applications by the end.
Final certification rate: 85%. But that's not what marked me most. What marked me is what I learned about myself on code and technical debt by watching beginners write it for the first time.
First shock: what I thought was "instinctive" in my code isn't. When I name a function fetchUserPortfolio, I know why I didn't write getData. But that knowledge is implicit. A learner writes getData and doesn't see the problem — because it works.
Clean code isn't an aesthetic ruleset. It's a communication contract between the code and the future reader (which will be you in 6 months). That contract isn't innate. It's learned by watching others' code break in production.
I had to verbalize things I'd never explained: why a 3-letter variable name in an 80-line function is a drama, why a 5-level nested if is debt, why a comment paraphrasing the code is noise.
First thing I hammered: code is read 10 times more than it's written. Not by marketing — by math.
A project lives 2–5 years in production. During that time, you re-read it:
If writing a function takes 10 minutes but making it readable takes 12, the initial overhead is recovered at the first debug.
I enforced this with a practical rule: every PR must be explainable aloud in 2 minutes. If the learner couldn't, the code was too dense, too confused, or did too much. Refactor before merge.
data, info, result, temp, val. Symptoms of "I don't know what to call it but it works."
Fix: always name by what it is, not by what it is technically. Not array, but activeUsers. Not fn, but computeMonthlyRevenue.
| 1 | // ❌ Vague |
| 2 | function getData(id: string) { |
| 3 | const res = await fetch(`/api/users/${id}`); |
| 4 | const data = await res.json(); |
| 5 | return data; |
| 6 | } |
| 7 | |
| 8 | // ✅ Precise |
| 9 | async function fetchUserProfile(userId: string): Promise<UserProfile> { |
| 10 | const response = await fetch(`/api/users/${userId}`); |
| 11 | return response.json(); |
| 12 | } |
I saw 200-line functions that validated, fetched, transformed, persisted and notified. All in one.
Rule: one function = one verb = one responsibility. If the name contains an "and", it's suspect. If you have to scroll to see the end, it's broken.
Practical limit taught: 30 lines per function, mandatory refactor at 50. Not a dogma, a warning signal.
if| 1 | // ❌ Pyramid |
| 2 | function processOrder(order) { |
| 3 | if (order) { |
| 4 | if (order.items.length > 0) { |
| 5 | if (order.payment) { |
| 6 | if (order.payment.status === "valid") { |
| 7 | // ... 40 lines of logic |
| 8 | } |
| 9 | } |
| 10 | } |
| 11 | } |
| 12 | } |
| 13 | |
| 14 | // ✅ Early returns |
| 15 | function processOrder(order: Order): OrderResult { |
| 16 | if (!order) return { ok: false, reason: "no_order" }; |
| 17 | if (order.items.length === 0) return { ok: false, reason: "empty_cart" }; |
| 18 | if (!order.payment) return { ok: false, reason: "no_payment" }; |
| 19 | if (order.payment.status !== "valid") return { ok: false, reason: "invalid_payment" }; |
| 20 | |
| 21 | // main logic, flat |
| 22 | } |
The if pyramid is slow sugar that piles up. Early returns flatten the code. Always reject first, process after.
| 1 | // ❌ Noise |
| 2 | // Increment the counter |
| 3 | counter += 1; |
| 4 | |
| 5 | // ❌ Liar: comment becomes wrong when code changes |
| 6 | // Fetch active users of the last 30 days |
| 7 | const users = await User.findAll({ where: { lastSeen: { [Op.gt]: thirtyDaysAgo } } }); |
| 8 | |
| 9 | // ✅ Comment the why, not the what |
| 10 | // 5-min cache to reduce DB load — see incident 2025-01-14 |
| 11 | const users = await User.findAll({ ... }); |
Rule taught: comments explain what code cannot. Don't paraphrase. If code is confused to the point of needing a comment to understand, refactor first, comment second.
| 1 | // ❌ What's this true? And this 30? |
| 2 | createUser("Alice", "alice@test.com", true, 30); |
| 3 | |
| 4 | // ✅ Object with explicit fields |
| 5 | createUser({ |
| 6 | name: "Alice", |
| 7 | email: "alice@test.com", |
| 8 | isAdmin: true, |
| 9 | ageYears: 30, |
| 10 | }); |
Practical rule: 3 parameters max, beyond that use an object. The call-site becomes self-documenting.
90% of learners arrived with an empty GitHub account or a single "Initial commit". Early imposed:
mainadd login form, not added login form)The goal wasn't git perfection. The goal was to anchor the culture of public code. Once they understood their code would be read, habits flipped.
One hour per day of forced pair programming (driver/navigator). At start, painful. After 2 weeks, visible transformation:
It's also where I saw the best developers emerge: not necessarily those who coded fastest, but those who asked the best questions.
I removed theoretical QCMs. Instead: an end-of-module project, defended orally before the cohort.
Criteria:
No bonus for "pure performance" or "advanced techniques". Just deliverable and communication quality.
This grid killed rote learning and forced understanding. The best learners weren't those who memorized the most syntax, but those shipping the clearest code.
Most surprising: beginners see technical debt better than we think. They just can't name it.
When a learner arrives saying "I don't understand my own code from yesterday", they wrote debt. When they say "it works but I don't know why", it's a signal to refactor before continuing.
I institutionalized the "daily refactor": every morning, 30 minutes, each learner re-reads yesterday's code and improves it. This practice alone changed the cohort's average level in 6 weeks.
Three things I changed in my code after this mission:
| Pitfall | Symptom | Fix |
|---|---|---|
| Theory without practice | Learners reciting without understanding | 70% practice minimum |
| Frameworks without foundations | Stuck when framework changes | Start with pure JS, add later |
| Theoretical evaluation | Rote memorization | Project-based evaluation |
| No Git early | Dangerous solo habits | Branches from day 3 |
| Copied tutorials without understanding | Stuck off the beaten path | Force solo debugging before help |
| No pair programming | Loneliness + accumulated debt | Daily forced sessions |
Teaching made me a better developer, more than any training I've taken. Verbalizing tacit choices, explaining why a name is bad, watching debt being created in real time — all of that changes how you write your own code.
If you get a chance to teach, take it. Not for the pay (often weak), not for the status (often ignored). For what it teaches you about your own craft.
Clean code isn't a code requirement. It's a future-reader requirement. And the future reader is you.
If this topic feels close to a real product problem, I can help on diagnosis, architecture, backend, interface and automations that make a platform usable in production.
Reader reactions
No comment yet
Be the first to share your reaction.