cswork / libraryOpen practice workspace

Articles

Thoughtworks Code Pairing Interview: Repository Preparation, Tests, and Refactoring

Prepare for Thoughtworks code pairing: set up the repository, preserve behavior with tests, make small changes, and explain refactoring and AI tool choices.

A Thoughtworks code pairing interview is best prepared for by making an unfamiliar codebase understandable and runnable, then practicing a small change with tests and a partner. If your invitation includes a repository, establish the existing behavior before guessing what feature the interviewer will request. The preparation goal is a reliable starting point, not a prewritten answer.

You may know the language well and still lose time discovering that the project needs a different runtime or that its tests depend on hidden state. This guide separates what Thoughtworks confirms from individual candidate reports, then walks through an original parcel-pricing exercise.

What Thoughtworks confirms about pairing

Official information: Thoughtworks' application-process page lists pairing among its hiring stages and says the activity can involve coding, system design, or architectural discussion. It also states that the process varies with the role and that preparation materials are sent in advance. That supports preparing for collaboration, but not assuming that every candidate receives the same coding repository.

The January 2026 Thoughtworks University recruitment article describes a coding challenge and pairing interview for developers. This is evidence about that graduate program, not a universal sequence for experienced hires.

Candidate reports: A 2026 early-career discussion describes cloning provided code and implementing a feature. A separate senior-consultant discussion describes advance repository preparation, a requested API change, and tests. The authors had different experience levels; their accounts are not a single standardized hiring loop.

Our inference: Reading existing code, preserving behavior, and responding to feedback are useful preparation priorities when your invitation describes repository-based pairing. Neither report establishes your preparation deadline, exact question, permitted language, interview length, or scoring rubric. Keep those details tied to your own instructions.

Prepare the repository without rewriting it

Start with the README and the invitation together. Identify the runtime version, package manager, dependency lockfile, startup command, test command, and any required local services. If a command fails, save the first actionable error and the environment version. A concise reproduction gives your coordinator something useful to investigate.

Once the project runs, trace one small input through the system. In an API repository, that might mean route, request validation, service call, persistence boundary, and response. In a command-line application, it might mean argument parsing, domain calculation, and formatting. Do not read every file equally deeply before you understand one complete path.

Write a short evidence map rather than an architecture essay:

Repository areaEvidence to recordQuestion to carry into pairing
Entry pointCommand or endpoint that exercises one behaviorIs this the intended interface for the change?
Domain logicFunction that makes the business decisionWhich existing rules must stay unchanged?
Test boundarySmallest test that reaches that decisionIs failure caused by behavior or environment?
External dependencyDatabase, clock, filesystem, or remote serviceShould the exercise isolate this dependency?

Keep observations separate from proposed improvements. “Two tests fail before any changes” is an observation. “The repository needs a new persistence abstraction” is a design proposal. The second requires justification and agreement; it should not distract from resolving the first.

If advance edits are not requested, leave the supplied starting point intact. You can prepare notes, learn the commands, and rehearse on your own code. Ask whether setup fixes should be committed, whether a clean baseline is expected, and whether the interview begins from the original branch. Those questions prevent avoidable ambiguity without attempting to predict the task.

An original practice exercise: extend a parcel quote

The following is an editorial exercise, not Thoughtworks interview material. It uses Python only to keep the behavior visible; your invitation may specify another language or framework.

Assume the existing service accepts a positive integer weight in grams. Standard delivery costs 500 minor currency units through 1,000 grams, including exactly 1,000. Heavier parcels cost 800. Invalid nonpositive weights raise an error. Input parsing and integer-type validation happen outside this small domain function.

def quote_base(weight_grams):
    if weight_grams <= 0:
        raise ValueError("weight must be positive")
    return 500 if weight_grams <= 1000 else 800

Before extending anything, explain the contract aloud. Weight uses grams, price uses integer minor units, and the threshold includes its upper endpoint. These details matter more than introducing a design pattern immediately.

Your partner now asks for express delivery, which adds 300 to either standard price. Existing callers should continue to receive the standard quote when they do not specify express delivery. Clarify whether the surcharge also applies to invalid input; in this exercise, validation still rejects invalid weight before returning any price.

A small implementation is enough:

def quote(weight_grams, express=False):
    return quote_base(weight_grams) + (300 if express else 0)

Notice the boundary choice. The new function composes a base price and surcharge while preserving the existing calculation. There is no new database, inheritance hierarchy, or configurable rule engine. If an existing codebase already has a pricing-policy interface, you would work within that design instead of importing this structure unchanged.

For a real API, the next concern is how the request represents express delivery. An omitted field and a false value should follow the agreed default. A string such as "false" must not accidentally become truthy through careless conversion. That is a request-validation question, separate from the domain arithmetic shown here.

Tests should explain the risk you are controlling

A regression is easy to miss: changing <= 1000 to < 1000 changes the price of a parcel at exactly the threshold. A normal lightweight parcel test would not catch it. Test the values on both sides and at the boundary, then test express delivery against the same contract.

CaseExpected quoteReason it belongs
Standard, 1 gram500Smallest valid positive input in this exercise
Standard, 1,000 grams500Inclusive threshold
Standard, 1,001 grams800First value in the heavier band
Express, 1,000 grams800Surcharge does not alter band selection
Express, 1,001 grams1,100Surcharge also applies to the heavier band
Zero or negative weightErrorInvalid input remains invalid

We ran the local Python exercise with eight unit tests, including default compatibility and both invalid-input cases. All eight passed. This verifies the illustrated functions, not a Thoughtworks repository, HTTP endpoint, database, or production deployment.

You can reproduce the central checks in a plain Python file containing both functions:

assert quote(1000) == 500
assert quote(1001) == 800
assert quote(1000, express=True) == 800
assert quote(1001, express=True) == 1100

These examples are useful because they connect the requirement to an observable result. A test named only test_success hides that connection. A name such as test_standard_price_includes_1000_grams lets a partner understand the decision before opening the implementation.

When a test fails, state what failed and what you have ruled out. “The boundary case returns 800 instead of 500; the express surcharge is not involved” gives your partner a precise place to help. Changing expected values simply to make the suite green would remove the evidence you need.

Refactor only when it makes the requested change clearer

A common preparation mistake is treating an unfamiliar repository as an invitation to redesign it. Refactoring is valuable when it makes behavior easier to understand or change while preserving the contract. It becomes a distraction when the new structure is larger than the requirement it serves.

In the parcel example, extracting quote_base is easy to explain because both standard and express pricing use the same rule. A hierarchy of delivery strategies might become useful with several genuinely different behaviors, but two price bands and one fixed surcharge do not prove that need.

Here is an illustrative pairing exchange:

Candidate: I could introduce a policy interface, but the current change only adds a fixed surcharge. I would keep the base calculation and add an explicit option.

Partner: What happens when another delivery mode arrives?

Candidate: I would first check whether it changes the price calculation or only adds another amount. If the behaviors diverge, we can extract a policy boundary then. For now, the boundary tests let us make that later change safely.

The point is not that simple functions always beat objects. It is that you can name the pressure that would justify a different design. A partner may reveal an existing convention or a forthcoming requirement that changes your decision. Acknowledge the new information and revise the plan explicitly.

When you do refactor, separate it from the feature if practical. First preserve behavior and run the relevant tests; then introduce the new behavior and add its tests. Smaller steps make a failure easier to attribute and make your reasoning easier to review.

Make collaboration and AI use visible

Thoughtworks' official recruitment AI policy encourages responsible use, asks candidates to disclose GenAI usage, and expects real-time skills and critical evaluation of generated outputs. That is not a reason to assume every tool or autonomous workflow is permitted in your specific session. Confirm the boundaries in the invitation or with the recruiter.

If a permitted tool suggests changing the threshold, you still own the resulting behavior. Compare the patch with the requirement, inspect the diff, and run the boundary tests. Explain which suggestion you accepted and why. A tool-generated explanation is not evidence that you understand the code.

During pairing, narrate decisions rather than every keystroke. Useful updates include “I am checking the default so existing callers remain compatible,” or “This refactor is unrelated to the feature; I will leave it out.” Pause when your partner asks a question. Collaboration includes making room for their interpretation of the requirement.

At the end, summarize the behavior added, the tests run, and the remaining uncertainty. For this exercise: express delivery adds 300; standard defaults and weight boundaries are preserved; HTTP parsing is outside the tested scope. That is a more informative handoff than announcing that the code is finished.

Sources and Further Reading