← Millennium Interview Insights

Millennium·Software Engineer·Technical Phone Screen·Intermediate

IntermediatePrefer not to say
May 2026

Summary

Technical phone screen for a Software Engineer role at Millennium. The question was a Python async debugging problem, pretty focused and hands-on, no fluff.

Questions Asked (1)

Q1

Given a broken Python async program that calls two HTTP APIs and processes the results, can you explain why it exits without printing anything, fix it so it runs correctly, and then describe how you'd run both API calls concurrently while handling cancellation and exceptions properly?

API & IntegrationsTechnical Trade-offsRoot Cause Analysis
Author's notes

Three parts to this and I fumbled the first one a bit.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by diagnosing the broken program: identify the missing event loop or unawaited coroutines causing silent exit, then fix it by properly running the async main with asyncio.run. Next, refactor to run both API calls concurrently using asyncio.gather with return_exceptions=True, and explain how to handle cancellation and exceptions robustly.

Pro tip: Emphasize that asyncio.gather with return_exceptions=True prevents one failure from cancelling other tasks, and always wrap the main coroutine in a try/except to handle KeyboardInterrupt and cleanup gracefully.

1. Diagnose the silent exit

Check if the async functions are defined but never awaited, or if the event loop is not running. Common causes: missing asyncio.run(main()) or calling coroutines without await.

2. Fix the basic execution

Wrap the main logic in an async def main() and run it with asyncio.run(main()). Ensure all coroutines are awaited and the event loop is properly closed.

3. Run API calls concurrently

Use asyncio.gather to schedule both API calls simultaneously. Optionally use asyncio.create_task for more control, and await them together.

4. Handle exceptions and cancellation

Pass return_exceptions=True to gather to collect exceptions without cancelling other tasks. Use try/except around await points and handle asyncio.CancelledError to perform cleanup.

5. Explain trade-offs and best practices

Discuss when to use gather vs. wait vs. TaskGroup, how to set timeouts with asyncio.wait_for, and the importance of structured concurrency for reliability.

Key Points to Mention

  • The event loop must be running to execute coroutines; asyncio.run() creates and manages it.
  • Unawaited coroutines produce a RuntimeWarning and never execute, leading to silent exit.
  • asyncio.gather runs awaitables concurrently and returns results in order.
  • return_exceptions=True in gather prevents one exception from cancelling other tasks.
  • Cancellation is cooperative: tasks must handle asyncio.CancelledError to clean up resources.
  • Use asyncio.wait_for or timeouts to avoid hanging on slow APIs.

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