Tennis-Refactoring-Kata: fast tests, smelly code, and a bug you are meant to find
This is a Refactoring Kata based on the rules of Tennis
At a glance
- What is it?
- The Tennis Refactoring Kata is an exercise rather than a library, built by Emily Bache. It hands you working code with design smells, a test suite you are not supposed to change, several versions to choose between, and a deliberate bug with the player names hard-coded.
- Who is it for?
- The Tennis Refactoring Kata fits a team or individual who wants a controlled place to practise refactoring with a safety net, and a trainer who needs the same exercise in thirty languages. It does not fit a project with real code to clean up, because the value here is in the feedback loop rather than the code that gets refactored.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 56 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The tests are the product, the code is the disposable part
The Tennis Refactoring Kata is a refactoring exercise based on the rules of Tennis, and the first thing to understand about it is that the deliverable is not the code. The deliverable is the practice.
The README says the test suite provided is fairly comprehensive and fast to run, and that you should not need to change the tests, only run them often as you refactor. That is a strong constraint and it is the point. A refactoring exercise without a trustworthy safety net teaches you to change code and hope, which is the habit the exercise exists to break. Here the tests stay fixed, so the only variable in the loop is your refactoring.
The code that goes with those tests is deliberately poor. The scenario explains that there are several versions of the kata, each with their own design smells and challenges, and the recommendation is to start with the first one, the class TennisGame1.
So the shape of the work is: take code that passes a comprehensive suite, make it better without breaking the suite, and keep running the tests as you go. The subject matter, tennis scoring, is small enough to hold in your head, which matters because the exercise is about the refactoring moves rather than about understanding a domain.
The scenario is an invoice, and that is why the refactoring has to be justified
The scenario section is the most quotable part of the README, and it explains the framing of the whole exercise.
You work for a consultancy company. A colleague has been doing work for the Tennis Society, and the contract is for ten hours of billable work. The colleague has spent eight and a half hours on it, then fallen ill. He says he has completed the work and the tests all pass. Your boss asks you to take over. She wants you to spend an hour or so on the code, so she can bill the client for the full ten hours, and she instructs you to tidy up the code a little and perhaps make some notes so you can give your colleague feedback on his chosen design. You should also prepare to talk to your boss about the value of this refactoring work over and above the extra billable hours.
That is a devious setup for a teaching exercise, because it gives you a reason to refactor that is not the reason a good engineer refactors. The boss needs the hour to be billable, which is not the same as the code needing an hour of work. The colleague's code passes the tests, which is not the same as the code being good.
The final line of the scenario, the instruction to prepare to talk about the value of the refactoring over and above the billable hours, is the real topic. Refactoring is only worth doing when the value exceeds its cost, and the exercise makes you argue that case in a setting where the easy answer is already handed to you.
Several versions, and you are told to start with the first
The kata ships in several versions and they are not variations on a theme. Each one has its own design smells and its own challenges, which means the same tennis rules can be expressed in ways that are unpleasant in different ways.
TennisGame1 is where the README tells you to start, and that ordering is a teaching decision rather than an arbitrary one. The first version is presumably the mildest in smell terms, and the later versions exist so that after you have finished the first one you have a second, harder problem waiting with the same test suite style.
The names of the versions show up in the videos too. The Guided Learning Hour recordings include a demo of TennisGame3 in C# in the Refactoring: What you need to know session, and a demo of TennisGame6 in C# in the Refactoring Skills: Extract Function session. That pairing is revealing about how the exercise is meant to be consumed: one session is about the general practice of refactoring, and the other is about a single specific move, Extract Function.
The point of a version ladder is that the same external behaviour can be preserved while the internal design changes completely, and that most of the refactoring moves are interchangeable. If your solution to TennisGame1 leans on Extract Function, it will probably serve you well on TennisGame3, and if it leans on something else you will find out why.
The player names are hard-coded, and the tests do not catch it
There is a deliberate error in several of the implementations, and it is worth reading the paragraph twice because the error is in the code but not in the behaviour the tests check. The player names are hard-coded to player1 and player2. The README notes that after you refactor, you may want to fix this problem and add suitable test cases to prove your fix works.
So the exercise has two layers. The first is the refactoring itself, with the existing suite as the safety net. The second is a TDD task that only becomes visible once the design is good enough to make the fix easy: a hard-coded value in the middle of a class is awkward to replace precisely because the surrounding design does not give it a natural home, and once you have introduced the right seam you can inject the name and write the test that proves it.
The sequencing is the lesson. You are not asked to fix the bug first, because fixing it first would be a different exercise, one about adding a feature to code you do not understand yet. You are asked to make the design good, and only then is the bug cheap to remove and testable.
There is also a quiet point about the tests being incomplete. The suite is comprehensive and it is still not sufficient, because a comprehensive suite over the observable behaviour cannot detect a hard-coded value if the behaviour it asserts happens to match. That is a realistic property of test suites, and the kata makes you feel it rather than telling you.
The same exercise in about thirty languages, and no build instructions
The repository root is a directory per language, and there are a lot of them: abap, angular, c, c_gtest, cpp, cpp-assert, crystal, csharp, dart, erlang, gleam, go, groovy, java, javascript, javascript-jest, julia, kotlin, objc, ocaml, perl, phel, php, python, ruby, rust, scala, swift, typescript, typescript-jest and vbnet.
The primary language of the repository is TypeScript, and the presence of both typescript/ and typescript-jest/ directories shows that even a single language is split by test runner. The same split appears for javascript/ and javascript-jest/. Some entries are variants rather than languages: c_gtest suggests a C build with GoogleTest, and cpp-assert suggests an assertion library rather than a framework.
That spread is the practical argument for the repository. A trainer running a workshop can hand out the exercise in whatever language the room writes, and a learner can practise in a language they already know instead of spending the exercise budget on syntax.
What the README does not do is tell you how to run anything. There are no install steps, no dependency commands and no test invocation, so the tooling is whatever the language subdirectory expects, and you should read that directory's own files before you start. For a language you already work in, that is a two minute job, and it is one of the reasons to start with a language you know rather than a language you want to learn.
There is a code reading exercise and a list of questions to argue about
Two extras sit alongside the refactoring, and both are easy to overlook.
The first is a code reading practice section. It points at a description of an exercise called Scanning for Code Smells, and says there are suitable lists of urls to open in some of the language subdirectories. So the repository does not only give you code to refactor, it also gives you code to read critically, which is a different skill: noticing smells without a compiler telling you.
The second is a set of questions to discuss afterwards, and they are carefully chosen. How did it feel to work with such fast, comprehensive tests? Did you make mistakes while refactoring that were caught by the tests? If you used a tool to record your test runs, review it: could you have taken smaller steps and made fewer refactoring mistakes? Did you ever make refactoring mistakes and then back out your changes, and how did it feel to throw away code? What would you say to your colleague if they had written this code? And what would you say to your boss about the value of this refactoring work, was there more reason to do it over and above the extra billable hour?
The third and fourth questions are the ones that turn an exercise into something a team can learn from, because they ask about your process rather than your code. The backout question in particular is aimed at the fear that makes people skip refactoring, which is the belief that a bad change is expensive to undo. In a kata with fast tests, undoing is cheap, and the discussion is where that lesson lands.
Where the kata is the wrong tool
The first wrong fit is real code with real users. A kata is a safe place to practise, and the value is entirely in the feedback loop and the discussion. The code you end up with is still a tennis scoring program with a hard-coded name bug, and refactoring it teaches you the moves without changing anything you own. If your goal is to clean up a module that is causing incidents, a kata is a warm-up, not the work.
The second wrong fit is a team that has no test suite. The exercise depends on the suite being comprehensive and fast, and on you not changing it. If the tests are slow, or if you need to add one to check what you just did, you have lost the safety net that makes the exercise safe, and the correct response is to fix the test suite first, not to push on with the refactoring.
The third is a solo learner who wants to be told the answer. The repository contains no solution and the README does not point to one. There are videos, including the two Guided Learning Hour sessions with C# demos of TennisGame3 and TennisGame6, and the author also publishes books and a coaching site. If you want a walkthrough of a specific version, you will find it in those videos, but the exercise itself is designed to be finished without one.
The fourth is a requirement for production code. Nothing here is a dependency you would add to a project, and the licence question is the easy part: the repository is MIT licensed with a license.txt file, so the code is free to use and adapt in any context, including in your own course content or in a workshop you run. The awkward part is that the deliberate error is a bug, and copying the code into a real project means copying the bug with it.
Editorial conclusion
The Tennis Refactoring Kata fits a team or individual who wants a controlled place to practise refactoring with a safety net, and a trainer who needs the same exercise in thirty languages. It does not fit a project with real code to clean up, because the value here is in the feedback loop rather than the code that gets refactored. Before starting, run the test suite for your language and read the smells in TennisGame1, and if the tests are not fast, that is the first thing to fix before touching the implementation.
Frequently asked questions
What is the Tennis Refactoring Kata?
It is a refactoring exercise based on the rules of Tennis, created by Emily Bache. It provides working code with design smells and a comprehensive, fast test suite that you should not change, and there are several versions, each with its own smells, starting with TennisGame1.
Can I change the tests while doing the Tennis Refactoring Kata?
Not as part of the exercise. The README says the test suite is comprehensive and fast to run, that you should not need to change the tests, and that you should run them often as you refactor. Adding tests is mentioned separately, for proving your fix to the hard-coded player names.
What is the deliberate bug in the Tennis Refactoring Kata?
In several of the implementations the player names are hard-coded to player1 and player2. The README notes that after refactoring you may want to fix that problem and add suitable test cases to prove the fix works.
Which languages does the Tennis Refactoring Kata support?
The repository has a directory per language, including abap, angular, c, cpp, crystal, csharp, dart, erlang, gleam, go, groovy, java, javascript, julia, kotlin, objc, ocaml, perl, phel, php, python, ruby, rust, scala, swift, typescript and vbnet, with some languages split by test runner.
What licence is the Tennis Refactoring Kata under?
The repository is MIT licensed, with a license.txt file at the root, so the exercise code can be reused and adapted freely, for instance in your own training material.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/emilybache-tennis-refactoring-kata)Was this useful?