The ExpenseReport legacy code refactoring kata in various languages.
This is an example of a piece of legacy code with lots of code smells. The goal is to support the following new feature as best as you can:
- Add Lunch with an expense limit of 2000.
- 📚 Read the code to understand what it does and how it works.
- 🦨 Read the code and check for design and code smells. Make a list of all code and design smells that you find.
- 🧑🔬 Analyze what you would have to change to implement the new requirement without refactoring the code.
- 🧪 Write a characterization test. Expand your list of code and design smells. Add those smells that you missed earlier and discovered now because they made your life writing a test miserable.
- 🔧 Refactor the code.
- 🔧 Refactor the test.
- 👼 Test-drive the new feature.
The ExpenseReport example currently exists in the following languages:
- Ada
- Assembler (Motorola 68020, Amiga OS, MaxonASM)
- Assembler (AArch64, Linux, GNU Assembler)
- Assembler (AMD64/Intel x86-64, Linux, GNU Assembler)
- bash
- BASIC (Amiga BASIC, Commodore Amiga) ⇐ Quite amazing! First BASIC without line numbers!
- BASIC (Bywater BASIC, Linux) (very similar to Commodore BASIC)
- BASIC (Commodore BASIC, Commodore 64)
- BASIC (Locomotive BASIC, Amstrad CPC)
- BASIC (Yabasic)
- C
- C#
- C++
- Clojure ⇐ This one was particularly painful to intentionally write poorly, I almost cried.
- COBOL
- D
- Dart
- Elixir
- F#
- Fortran
- Go
- Groovy
- Haskell
- Java
- JavaScript
- Julia
- Kotlin
- Lisp (Common Lisp)
- Lua
- Nim
- Objective-C
- Pascal
- Perl
- PHP
- PostScript
- Prolog
- Python
- Raku (Perl6)
- Rexx (tested with Regina Rexx and ARexx)
- Ruby
- Rust
- Scala
- Scheme
- Smalltalk
- SQL (Using SQLite3)
- Swift
- TcL
- TypeScript
- Visual BASIC
- XML/XSLT
- Zig
- zsh
(in no particular order and with no guarantee)
- ABAP
- Amiga E
- AMOS BASIC
- APL
- More Assembler implementations (arm, aarch64, i686, powerpc, powerpc64, mpis, mips64, sparc, sparc64, risc-v, etc, usually on Linux)
- Carbon
- CLIPS
- Cluster
- Concurnas
- Curry
- Dark
- Eiffel
- Elm
- Erlang
- Flix
- Forth
- Gleam
- Gosu
- Hack
- Hare
- Java Byte Code / JVM Assembler
- Lobster
- Logo
- Mercury
- Modula-2 (once the linker starts working again)
- Oberon
- OCaml
- Pony
- PureScript
- R
- Reason
- REBOL
- Scratch
- SNOBOL
- Solidity (maybe)
- TeX
- Unison
- WebAssembly
- Brainfuck
- False
- Malbolge
- Whitespace
There are many more programming languages than in the list above. Find a more complete list on Wikipedia.
- Make sure that all languages are providing the identical challenge. To be practical, this will require the removal of the timestamp side-effect.
- Provide the time-stamp side-effect on a separate branch.
- Provide a test setup (without test) on a separate branch so that folks can choose whether they want to include the setup work in the kata or not.
- Provide a level 2 challenge for creating an HTML report besides the Plain Text report.
To see solutions, switch to the solutions branch.
Warning The solutions branch will be rebased!
I first encountered the ExpenseReport example during a bootcamp at Equal Experts. I also have seen the ExpenseReport example being used by Robert "Uncle Bob" C. Martin. However, he seems to not be the original author (https://twitter.com/unclebobmartin/status/1537063143326855176?s=20&t=lh_vVb9jUQmY6PYG50974w) I have tried to research its origins but so far I have failed. If you know who has first come up with this example, please get in touch with me.
As I was asked for the license conditions, I have decided to put this under CC-BY-SA, with the following considerations:
- Please credit me, last but not least to retain the opportunity for people to experience this exercise in as many programming languages as they are interested in.
- Share it under the same license.
- Using this as training material should be allowed, even if the trainer is charging money for their time.
Disclaimer: As stated above, I did not come up with the original version. The original author is unknown to me. The CC-BY-SA license thus can only cover my contribution: Recreating the Java version from memory, creating the versions in other programming languages than Java, and keeping them consistent.