Clean Code Mastery: exercises
Reading about clean code and doing it are different skills. Every module has a hands-on exercise: a working but messy TypeScript file with a passing test suite that pins its behaviour. You refactor it until it reads clean without turning a test red. Clone the repo, run the tests, and check your work against the reference solution.
How the exercises work
Each exercise ships a working but messy TypeScript file and a passing test suite that pins its behaviour. Your job is to refactor the code clean without turning a test red. A reference solution is checked in for every one.
git clone https://github.com/ehsangazar/self-paced-clean-code-mastery.git
pnpm install
pnpm test # run every module
pnpm test 03 # run one moduleMake an unreadable function readable
A working checkout function with single-letter names, magic numbers, and three levels of nesting. Refactor it for a reader without changing what it returns.
Open on GitHubRename so intent needs no explanation
A function whose every name is cryptic or actively misleading, including a variable called list that holds a count. The logic is easy once the names are honest.
Open on GitHubSplit a function that does seven things
One function that validates, computes, formats, and logs at once, and mixes a command with a query. Break it into single-purpose pieces.
Open on GitHubDelete the comments, keep the meaning
A function drowning in redundant, outdated, and commented-out noise, with one genuine why-comment buried in it. Make the code self-documenting and keep only the comment that earns its place.
Open on GitHubGroup and order code for the reader
A module whose structure fights the reader: helpers scattered, the entry point buried. Reorder and group it, changing nothing about what it does.
Open on GitHubFlatten nesting with guard clauses
An access check nested five levels deep, with each denial reason buried in a distant else. Flatten it into guard clauses while preserving every decision.
Open on GitHubShrink scope and kill mutable state
A function leaning on wide mutable variables reused for two purposes, plus bare magic numbers. Tighten the scope, prefer values that do not change, and name the constants.
Open on GitHubRefactor toward dependency inversion
A dispatcher that news up its own dependencies and switches on a type string. Introduce an interface and inject the channels so a new one needs no edits to the dispatcher.
Open on GitHubFail fast at the right boundary
A money transfer that validates late and throws one vague error for everything. Move validation to the boundary with guard clauses and typed, specific errors.
Open on GitHubWrite tests that read as a specification
Clean production code paired with a deliberately bad test suite. This one inverts the format: the deliverable is the tests, rewritten to read as a spec.
Open on GitHubCover untested code, then change it safely
Gnarly untested legacy code with surprising edge cases. Write characterization tests to build a safety net, then refactor behind it.
Open on GitHubReview a change and argue the trade-offs
A real pull request that works but oversteps its ticket. Write the review: what blocks, what is a nit, and how to push back on the scope creep.
Open on GitHub