← Capital One Interview Insights

Capital One·Data Scientist·Technical Phone Screen·Intermediate

Intermediate
Apr 2026

Summary

Capital One data scientist interview that was basically a live code review session on a buggy Python cart module. They handed you a repo, told you to run the tests, and then asked you to diagnose, fix, and restructure everything. Pretty thorough for what I expected to be a lighter technical screen.

Questions Asked (5)

Q1

Walk through what this add_item function does on a single call versus across multiple calls, and identify at least three concrete design problems that could cause flaky tests or poor readability.

Root Cause AnalysisTechnical Trade-offs
Author's notes

This was the meaty one.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

First, describe the function's behavior on a single call, then contrast it with its cumulative effects across multiple calls, emphasizing state changes. Next, systematically identify design flaws that lead to flaky tests or poor readability, linking each to concrete consequences. Finally, propose improvements or alternatives to demonstrate problem-solving skills.

Pro tip: Frame the design problems in terms of testability and maintainability, and suggest how you would refactor the function to make it pure or idempotent, showing you think beyond just identifying issues.

1. Single-call behavior

Explain what add_item does when called once: inputs, outputs, side effects, and any state mutations. Be precise about return values and exceptions.

2. Multi-call behavior

Describe how repeated calls affect internal state or external systems, highlighting cumulative effects, order dependence, and potential for race conditions.

3. Identify design problems

List at least three concrete issues (e.g., global state, hidden dependencies, lack of idempotency) and explain how each causes flaky tests or reduces readability.

4. Propose improvements

Suggest refactoring ideas such as making the function pure, using dependency injection, or adding clear documentation to mitigate the identified problems.

Key Points to Mention

  • Global or mutable state leading to test pollution and order-dependent failures
  • Lack of idempotency causing duplicate side effects on retries
  • Hidden dependencies (e.g., on time, random, or external services) that make tests non-deterministic
  • Poor naming or unclear side effects reducing readability
  • Absence of input validation or error handling leading to unpredictable behavior
  • Concurrency issues if the function is not thread-safe

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

Write a minimal pytest test that reliably exposes the shared mutable default bug for the discounts parameter. No external files, just plain asserts.

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

Called add_item twice with different discount lists and checked that the second call didn't inherit state from the first.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

First, identify the function under test that uses a mutable default argument (e.g., `def apply_discounts(price, discounts=[])`). Then write a minimal pytest test that calls the function twice without passing the `discounts` argument, and assert that the second call does not include discounts from the first call. Use plain asserts to check the expected behavior.

Pro tip: Mention that mutable default arguments are evaluated once at function definition time, so the same list object is reused across calls. This shows deep Python knowledge and awareness of a common pitfall.

1. Identify the function and its mutable default

Locate the function that has a mutable default argument for `discounts`, such as a list or dictionary. Confirm that the default is indeed mutable (e.g., `[]` or `{}`).

2. Design the test to expose the bug

Call the function twice without providing the `discounts` argument. The first call should modify the default list (e.g., by appending a discount). The second call should ideally start with a fresh list, but due to the bug, it will retain the modification.

3. Write the pytest test with plain asserts

Create a test function that performs the two calls and asserts that the second call's result does not include the discount added in the first call. Use `assert` statements to check the expected behavior.

4. Run the test and observe failure

Execute the test with pytest. It should fail, demonstrating the shared mutable default bug. The failure message will show that the second call unexpectedly includes the first call's discount.

Key Points to Mention

  • Mutable default arguments are evaluated once at function definition time, leading to shared state across calls.
  • The test should call the function twice without passing the `discounts` argument to trigger the bug.
  • Use plain asserts to check that the second call does not include discounts from the first call.
  • The test should be minimal and self-contained, with no external files or dependencies.
  • Explain that the bug can be fixed by using `None` as the default and initializing the list inside the function.
  • Demonstrate awareness of Python's evaluation strategy for default arguments.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

Refactor add_item to remove global state and mutable defaults. Improve type safety and readability, maybe using dataclasses or typed dicts. Justify your API changes.

System DesignTechnical Trade-offs
Author's notes

I went with a typed dict for the cart and made the function take an explicit cart argument with no fallback to a global.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by identifying the problems with the current add_item function: global state and mutable default arguments. Then propose a refactored version that uses a dataclass or TypedDict for type safety and readability, and explain how the API changes improve maintainability and testability. Finally, justify the trade-offs, such as increased verbosity versus clarity, and how the changes align with best practices and the needs of a data science team.

Pro tip: Emphasize that removing mutable defaults prevents subtle bugs that are hard to debug, and using dataclasses or TypedDicts makes the code self-documenting, which is crucial for collaboration in data science projects. Also, mention that these changes facilitate unit testing and integration with type checkers like mypy.

1. Identify issues

Point out the problems with global state and mutable default arguments, such as unintended side effects, difficulty in testing, and potential bugs.

2. Propose refactoring

Suggest replacing global state with parameters or a class, and mutable defaults with None or a sentinel value. Introduce a dataclass or TypedDict to encapsulate the item data.

3. Improve type safety

Explain how using type hints and dataclasses/TypedDicts enhances type safety, enables static analysis, and reduces runtime errors.

4. Justify API changes

Discuss how the new API is more explicit, easier to use correctly, and aligns with Pythonic principles. Mention trade-offs like potential breaking changes and migration strategies.

5. Demonstrate benefits

Highlight improvements in readability, maintainability, and testability. Provide a brief example of how the refactored function would look and be used.

Key Points to Mention

  • Avoid mutable default arguments (e.g., use None and initialize inside the function).
  • Eliminate global state by passing dependencies explicitly or using a class to encapsulate state.
  • Use dataclasses or TypedDict for structured data with type hints.
  • Leverage type hints and static type checking (e.g., mypy) to catch errors early.
  • Consider backward compatibility and provide a migration path for API changes.
  • Emphasize testability: pure functions and explicit inputs make unit testing easier.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q4

How would you organize the test suite and fixtures for this module? How do you handle mocking time or randomness to keep tests deterministic?

A/B Testing & ExperimentationTechnical Trade-offs
Author's notes

Talked about using a fresh cart fixture via pytest's fixture scope, monkeypatching any time-dependent calls.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by describing a layered test suite (unit, integration, end-to-end) and how fixtures provide reusable, isolated data. Then explain techniques for controlling time and randomness, such as dependency injection, freezegun, and seeded random generators, to ensure deterministic tests.

Pro tip: Emphasize that deterministic tests are critical for A/B testing pipelines because non-determinism can mask real treatment effects or create false positives. Mention that you version-control test data and use property-based testing for edge cases.

1. Define test layers and scope

Outline unit tests for individual functions, integration tests for module interactions, and end-to-end tests for the full pipeline. Explain what each layer covers and why they are separated.

2. Design fixtures for isolation and reuse

Use pytest fixtures or similar to create fresh, minimal datasets for each test. Ensure fixtures are scoped appropriately (function, module, session) and avoid shared mutable state.

3. Control time and randomness

Inject time and random number generators as dependencies, or use libraries like freezegun and seeded random. For A/B tests, fix random seeds and mock time to simulate experiment durations.

4. Ensure determinism in data and environment

Version-control test data, set fixed random seeds, and mock external services. Use deterministic sorting and avoid relying on system time or unordered collections.

5. Validate and maintain test suite

Run tests in CI with fixed environments, monitor for flakiness, and refactor fixtures as the module evolves. Use coverage tools to ensure critical paths are tested.

Key Points to Mention

  • Layered testing strategy: unit, integration, end-to-end
  • Fixture scoping and isolation to prevent test pollution
  • Dependency injection for time and randomness
  • Use of freezegun, pytest-mock, and seeded random generators
  • Deterministic data versioning and environment control
  • Handling flaky tests and maintaining test reliability in CI

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q5

What exact shell commands would you run to set up the environment, pin dependencies for reproducibility, and run tests with coverage both locally and in CI?

System DesignAPI & Integrations
Author's notes

pip freeze into requirements.txt, pytest with --cov flag, nothing surprising.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by outlining a reproducible environment setup using a package manager like conda or pip with pinned dependencies, then describe how to run tests with coverage locally and in CI. Emphasize the importance of version control and automation to ensure consistency across environments.

Pro tip: Mention using a lock file (e.g., conda-lock or pip-tools) to pin exact versions and hashes, and highlight that CI should mirror the local setup to catch environment-specific issues early.

1. Set up the environment

Create and activate a virtual environment or conda environment, then install dependencies from a pinned requirements file.

2. Pin dependencies for reproducibility

Use tools like pip-tools or conda-lock to generate a lock file with exact versions and hashes, ensuring consistent installs.

3. Run tests with coverage locally

Execute tests using pytest with coverage flags, and generate a coverage report to verify code coverage.

4. Configure CI pipeline

Set up a CI workflow (e.g., GitHub Actions) that installs dependencies from the lock file and runs the same test command with coverage.

5. Ensure consistency and automation

Use environment variables and CI caching to speed up builds, and ensure the CI environment matches local as closely as possible.

Key Points to Mention

  • Use of virtual environments (venv, conda) to isolate dependencies
  • Pinning dependencies with exact versions and hashes using pip-tools or conda-lock
  • Running tests with pytest and coverage.py, e.g., `pytest --cov=src --cov-report=xml`
  • CI configuration (e.g., GitHub Actions) that installs from lock file and runs tests
  • Caching dependencies in CI to reduce build time
  • Ensuring reproducibility by committing lock files to version control

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.