Python from First PrinciplesPost 18 of 20 · 20 of 20 written
- How to use this guide
- 01What Python actually is
- 02Names, objects and references
- 03Built-in types and what they cost
- 04Control flow and expressions
- 05Functions in depth
- 06Iteration and generators
- 07Classes and the data model
- 08Context managers and exceptions
- 09Modules, packages, imports and environments
- 10Type hints and Pydantic
- 11The performance model
- 12Concurrency and parallelism
- 13NumPy: the array model everything else copies
- 14Data and serialization
- 15PyTorch mental model
- 16AI systems engineering in Python
- 17Tooling, debugging and testing
- 18Asking the right questions
- 19Roadmap and build-to-learn projects
- 20Glossary and resources
18. Asking the right questions
Python · 18 of 202 min readOct 2026
Python · Post 18 of 202 min readOct 2026
Fluency means knowing which question exposes the problem. Use this part as a checklist whenever you read, review or prompt for code, whether you or an AI assistant wrote it.
Nine questions that cover most bugs
- Objects: what does this name point to, who else references it, and is it mutated here? (Part 2)
- Types: what type is this value, and which special method does this syntax call? (Parts 3 and 7)
- Shapes: what are the shape, dtype and device after each step? (Parts 13 and 15)
- Cost: does the time go to the interpreter or to compiled code, and what is the complexity? (Parts 3 and 11)
- Waiting: is this I/O-bound or CPU-bound, and does the concurrency model match? (Part 12)
- Failure: what happens on a timeout, bad input, partial failure or a crash mid-write? (Parts 8 and 16)
- Trust: where does untrusted data enter, and where is it validated? (Parts 10, 14 and 16)
- Environment: which interpreter, which package versions, which CUDA build? (Part 9)
- Evidence: which test or measurement shows this works? (Parts 11 and 17)
Red flags in AI-written Python
except Exception: pass, or broad excepts that quietly return a default.- Mutable default arguments, and mutable state at module level.
if x:where 0, an empty string or an empty list is a valid value.- Blocking calls (
requests,time.sleep, sync SDK clients) insideasync def. - Network calls with no timeout; retries with no backoff; retries stacked on top of an SDK’s own retries.
- Python loops over array or tensor elements;
.item()or.cpu()inside the training loop. pickle.loadortorch.loadon outside files;evalorexecon model output.- API keys, paths and model names hard-coded across files instead of one settings object.
- Layers kept in a plain list inside an
nn.Module; evaluation withoutmodel.eval()ortorch.no_grad(). - Invented APIs: methods or parameters that do not exist in your installed version. Check with
help()or the docs for that version. - Unstated shape assumptions, with no assertions or shape comments.
- Tests that only cover the happy path, or mock so much that nothing real is tested.
Prompting coding assistants as the architect
- Specify the contract: inputs, outputs, types, error behavior and performance limits. A function signature or Pydantic model is the clearest spec.
- State the environment: Python version, library versions, sync or async, CPU or GPU.
- Ask for tests with the code, including failure cases.
- Ask what could break it and what it assumes about the inputs, before you accept it.
- Ask for the smallest change that works, and review diffs rather than whole files.
- Report failures fully: the complete traceback and exact versions, not a summary.
Turning confusion into a good question
A weak question: “PyTorch training doesn’t work.”
A strong question: “My 10-class MLP’s loss stays at 2.30. I printed named_parameters() and all layers are registered. The order is zero_grad, backward, step, with lr 3e-4. Gradients on fc1.weight are all zero. What could zero the gradients before the first layer?”
The strong version gives the symptom with numbers, says what is already ruled out, and asks one narrowed question. Even the number is a clue: 2.30 is ln(10), the loss of a model that guesses uniformly across 10 classes.