String Transformer
Level: Advanced 60–90 minConcepts: AlgorithmsDesign PatternsEdge CasesRefactoring
Solutions: C# | TypeScript | Python
Build a string transformer that supports chainable operations. Each operation transforms the input string and passes the result to the next operation.
Requirements
- Create a
StringTransformerthat takes an initial string - Support the following operations, all chainable:
capitalise()— capitalise the first letter of each wordreverse()— reverse the entire stringremoveWhitespace()— remove all whitespacesnakeCase()— convert to snake_casecamelCase()— convert to camelCasetruncate(n)— truncate to n characters, adding ”…” if truncatedrepeat(n)— repeat the string n times with a space separatorreplace(target, replacement)— replace all occurrences
result()returns the final transformed string- Operations are applied in order
Test Cases
| Input | Operations | Result |
|---|---|---|
| ”hello world” | capitalise() | “Hello World" |
| "hello world” | reverse() | “dlrow olleh" |
| "hello world” | removeWhitespace() | “helloworld" |
| "hello world” | snakeCase() | “hello_world" |
| "Hello World” | camelCase() | “helloWorld" |
| "hello world” | truncate(5) | “hello…" |
| "hello world” | truncate(50) | “hello world" |
| "ha” | repeat(3) | “ha ha ha" |
| "hello world” | capitalise().reverse() | “dlroW olleH" |
| "hello world” | snakeCase().capitalise() | “Hello_World" |
| "" | capitalise() | "" |
| "HELLO WORLD” | camelCase() | “helloWorld" |
| "hello-world test” | snakeCase() | “hello_world_test” |
Bonus
- Add
cipher(n)— apply a Caesar cipher shifting each letter by n positions - Add
slug()— convert to URL-friendly slug (lowercase, hyphens, no special characters) - Add
mask(n)— mask all but the last n characters with asterisks (useful for credit cards, emails) - Make the transformer immutable — each operation returns a new transformer, allowing branching transformations
Reference Walkthrough
Reference implementations in C#, TypeScript, and Python live at tddbuddy-reference-katas/string-transformer. This is an F2 (light builder) kata showcasing the Strategy pattern: every transformation (Capitalise, Reverse, SnakeCase, Truncate(n), Replace(target, replacement), …) is a self-contained class implementing a uniform apply(string) -> string; Pipeline runs them in order; a small test-folder PipelineBuilder makes each scenario a one-line fluent chain.
Scope note — eight required transformations only. The reference implements the eight chainable operations from the spec (capitalise, reverse, removeWhitespace, snakeCase, camelCase, truncate, repeat, replace). The four bonus ideas — cipher(n), slug(), mask(n), and immutable-branching pipelines — are out of scope and listed as stretch goals in the repo README.
- C# (.NET 8, xUnit, FluentAssertions 6.12.0) — walkthrough
- TypeScript (Node 20, Vitest 1.6, TS 5 strict) — walkthrough
- Python (3.11, pytest) — walkthrough
This kata ships in Agent Full-Bake mode at middle gear, the F2 (light builder) tier. See the repo’s Gears section for why middle gear is the deliberate choice.
Related reading
- Katas Are Rehearsal, Not Performance
You don't practice TDD on production code. You practice on katas and bring the muscle memory to production. The gap between knowing TDD and doing TDD is reps. - Property-Based Tests Are the Guardrail Agents Need
Example-based tests encode cases an agent can enumerate and hardcode past. Property-based tests quantify over inputs the agent did not see at code-write time, which is the gap that makes the lookup-table attack more expensive than the honest fix. Properties are one layer in the enforcement stack, not the whole stack. - Agents Amplify Whatever Vocabulary They Find
Agents do not bring vocabulary to your codebase. They mirror what's already there. Strong vocabulary gets agent contributions that read like the team. Weak vocabulary gets faster mediocrity. The work you did for craft reasons just became leverage.