writing_good_code#

  • Series: lecture-python-programming

  • File: lectures/writing_good_code.md

  • Audit date: 2026-08-26

  • Corpus snapshot: ceec881028

  • Categories audited: writing, math, code, figures, links, admonitions (JAX out of scope)

  • Overall score: 7.8 / 10

  • Priority: HIGH

Score breakdown#

Category

Score

One-line note

Writing

3/10

qe-writing-006 ×9; qe-writing-005 ×6; qe-writing-003 ×6, +4 more.

Math

9.5/10

qe-math-009 ×2.

Code

7/10

qe-code-001 ×12.

JAX

out of scope

JAX rules target lecture-jax.

Figures

7.5/10

qe-fig-005 ×4; qe-fig-008 ×4; qe-fig-001 ×2.

References

N/A

no citations in this lecture.

Links

10/10

no mechanical violations detected.

Admonitions

10/10

no mechanical violations detected.

Issues#

Critical#

None found.

High severity#

  • [qe-code-001] (reviewer) — Follow PEP8 unless closer to mathematical notation. Count: 12. Lines: 253, 259, 270, 277, 347, 438, 444. Example: first, a warning for anyone acting on this report: the deliberately poor cell at 73-134 is not a qe-code-001 site. The lecture’s claim at 136 that it “more or less follows PEP8” is exactly right - I ran flake8 over every code cell in the file and that cell is clean; its faults are structural, which is the point being made. The real items are in the code the lecture offers as good. 438 repeats logic the class has just factored out: q_star = np.exp(self.α * p_star) - self.β is self.qs(p_star), defined four lines above at 428-429 and used correctly at 449 - a DRY violation inside the lecture’s DRY example. 444 carries the # Now plot comment over from the bad code into the refactored method. compute_equilibrium (434-441) prints its two results rather than returning them, so nothing downstream can use the equilibrium it computes. The six f'...{p_star: .2f}' format specs (347, 348, 384, 385, 440, 441) all carry a leading space in the format spec, which is the space flag for positive numbers and renders a stray gap after “is”. 259 and 277 wrap for targets in redundant parentheses (for (α, s, δ) in product(...), for (ax, params) in zip(...)). 253 and 270 are the two blank-line counts flake8 reports in the good cell (one blank line before the def, one after its body).

  • [qe-writing-003] (reviewer) — Maintain logical flow. Count: 6. Lines: 228, 236, 270, 288, 304, 397. Example: the refactored example does not deliver two of the four things the precepts asked for, and then claims it did. 288 states “Magic numbers are avoided”, and 270 still writes plt.subplots(3, 1, figsize=(8, 16)) with the literal 3 that 158 singled out by name (“numbers like 50 and 49 and 3 scattered through the code”) - and the 3 is now load-bearing in a second place, since it must match the number of parameter sets defined at 273-275. The “Minimize Global Variables” precept (196-213) and the JIT argument built on it (215-224) fare no better: 287 claims globals are “quarantined by collecting them together at the end”, which is true of set_one, set_two, set_three, fig and axes, and the section that motivated the whole discussion - type inference for JIT compilation - is never revisited, nothing is jitted, and the improved code still depends on np and plt imported inside the bad cell at 74-75, so it cannot run on its own. Second, 228 uses the term WET (“the evils of global variables and WET code”) and 230 explains it two lines later in a single-item bullet. Third, the section headed “#### Which One, Functions or Classes?” (236-244) never answers its own question: “Both can be useful”, “We’ll learn more about these topics over time”, “(Personal preference is part of the story too)”, “What’s really important is that you use one or the other or both” - four sentences, no criterion. Fourth, the exercise’s second plot cell contains an unflagged bug: 390 builds p_grid, 393-394 compute qs and qd from it, and 397-398 then plot them against grid from the earlier cell at 355, leaving p_grid unused; it renders correctly only because both are np.linspace(2, 4, 100), and the solution at 448-449 silently repairs it without saying so. Fifth, the exercise reuses \(\alpha\) and \(\delta\) for the supply and demand parameters (304, 310) after {eq}`gc_solmod` and 61 have given both letters different meanings in the same lecture, and both meanings coexist in one notebook namespace (α is a tuple at 85 and a float at 425).

  • [qe-writing-005] (reviewer) — Use bold for definitions, italic for emphasis. Count: 6. Lines: 150, 160, 182, 188, 198, 230. Example: bold is used three times in the lecture and all three are emphasis, not definition: “coding style matters a great deal” (150), and “your aim should be to automate repetition, not do it yourself” (188) - the rule asks for italic here, and no italic appears anywhere in the file. Meanwhile the four terms the lecture actually defines are all unbolded: “magic numbers” arrives in quotation marks (160) despite having its own heading; “Don’t repeat yourself (DRY)” is introduced as plain text mid-sentence (182); “global variables” is glossed inside a parenthesis, “(i.e., names assigned to values outside of any function or class)” (198); and WET is defined in a single-item bullet (230), two lines after it is first used. The one arguably correct bold, parameters at 314, is in the exercise and restates a gloss already given at 61.

  • [qe-writing-006] — Capitalize lecture titles properly. Count: 9. Lines: 45, 142, 156, 176, 196, 215, 226, 236, 246. Example: H2 Title Case: ‘An Example of Poor Code’ (Example, Poor, Code).

Medium severity#

  • [qe-fig-001] — Do not set figure size unless necessary. Count: 2. Lines: 80, 270. Example: figsize=.

  • [qe-fig-005] — Descriptive figure names for cross-referencing. Count: 4. Lines: 73, 250, 353, 388. Example: code-cell figure without mystnb figure metadata.

  • [qe-fig-008] — Use lw=2 for line charts. Count: 4. Lines: 91, 108, 125, 263. Example: plot() without lw=.

  • [qe-math-009] (reviewer) — Choose simplicity in mathematical notation. Count: 2. Lines: 304, 310. Example: the exercise re-uses both Greek letters the main example has already spent. {eq}`gc_solmod` at 54 and the gloss at 61 make \(\alpha\) a productivity parameter and \(\delta\) a depreciation rate; 304 then makes \(\alpha\) the exponent in the supply curve \(q_s(p) = \exp(\alpha p) - \beta\) and 310 makes \(\delta\) the demand elasticity in \(q_d(p) = \gamma p^{-\delta}\). Since the two problems share one notebook namespace, both meanings are simultaneously live in the code - α is the tuple (0.25, 0.33, 0.45) at 85 and the float 0.1 at 425 - and the simpler option was to pick letters the lecture had not used. The rest of the notation is commendably plain: \(k_t\), \(s\), \(q_s\), \(q_d\), \(h\), \(p^*\), \(q^*\), with no decorative letters anywhere.

  • [qe-writing-001] — Use one sentence per paragraph. Count: 3. Lines: 27, 319, 327. Example: 2 sentences in one paragraph.

  • [qe-writing-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 4. Lines: 162, 194, 230, 240. Example: the lecture defers four times and delivers on two: “We’ll talk about how to avoid repetition below” (194), “(We’ll discuss how just below)” (213), “We’ll learn more about these topics over time” (240) and “What’s really important is that you use one or the other or both” (244) - the last two close the section that was supposed to choose between functions and classes. 162 (“This is not a compliment.”) is a one-line aside on the word “magic”. Two single-item bullet lists are used as footnotes rather than lists: “* Also called DIE (duplication is evil).” (184) and “* WET stands for “we enjoy typing” and is the opposite of DRY.” (230). And the moralising register accumulates without adding information - “mortal sin” (178), “not all evil” (164), “the evils of” (228), “shaming ourselves” (234), “Rookie programmers … with abandon — as we once did ourselves” (200) - five figures for one idea.

  • [qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 2. Lines: 142, 270. Example: a lecture about how to write code argues for 100 lines without showing any. “Good Coding Practice” and its four subsections (142-244) state every precept in prose - replace 50 and 49 with time_series_length and time_series_length - 1 (167-169), factor the repeated loop into a function (176-194), sandbox variables so JIT type inference can work (215-224), prefer functions or classes (226-244) - and not one is shown as a two-line before-and-after, although the bad cell above supplies the “before” for all four. The magic-number fix in particular is described in words at 167-169 where three lines of code would settle it. Second, the lecture’s central comparison is between two cells whose output is identical, and it prints that output twice: two 8x16 three-panel figures, 170 lines apart (73-134 and 250-282), so the reader scrolls past two full pages of the same picture. 248 says the second cell “reproduces the plot above”, which is the right claim, and the figure is then the least informative thing on the page - a {note} saying the outputs match, with the second figure suppressed or reduced, would put the attention back on the code. Relatedly the lecture has no admonitions at all beyond the epigraph, where the JIT aside at 215-224 is exactly the kind of technical digression a {note} exists for and is currently promoted to an H4.

  • [qe-writing-008] — Remove excessive whitespace between words. Count: 2. Lines: 148, 207. Example: 2 spaces.

Low severity#

None found.

Strengths#

  • The lecture is built as one worked comparison rather than a list of rules: a deliberately poor program (73-134), a diagnosis of what is wrong with it in four named precepts (142-244), and the same program rewritten (250-282) - and the diagnosis refers back to specific lines of the bad version (“numbers like 50 and 49 and 3”, 158; “the loop to generate time series”, 180).

  • The poor example is poor in exactly the intended dimension and no other. 136-138 draws the distinction explicitly - “True, the code more or less follows PEP8 … At the same time, it’s very poorly structured” - and the claim holds: flake8 reports nothing on that cell, so the reader is looking at bad structure rather than bad formatting, which is the whole pedagogical point.

  • The refactored version demonstrates the two fixes it is meant to demonstrate cleanly: itertools.product (251, 259) replaces three near-identical nested loops with one, and time_series_length=50 (253) turns the two magic numbers 50 and 49 into one named default and time_series_length-1 (261).

  • 284-289 makes the lecture check its own work - four bullets stating what the reader should be able to see in the new code - which is the right instinct even where two of the four claims overshoot (see the qe-writing-003 note).

  • The plot_path helper is given a docstring stating what it does to what (254-256, “Add a time series plot to the axes ax for all given parameters”), and it takes ax as its first argument rather than reaching for a global figure - the pattern the globals section argues for, shown in practice.

  • The JIT subsection (215-224) gives a scientific-computing reason for a rule usually justified on maintainability grounds, and states the mechanism rather than the slogan: “the type inference required for JIT compilation is safer and more effective when variables are sandboxed inside a function”.

  • The exercise is well chosen because its repetition is of a different shape from the lecture’s: four cells that duplicate a model, a solver, a plot and then all three again with one parameter changed (337-405), so the reader cannot simply copy the itertools.product trick and must reach for a function or a class.

  • The solution’s Equilibrium class puts the two curves, the root-finding and the plot behind one parameter set (423-456), and the payoff is then demonstrated in three lines - eq.γ = 1.25 and re-run (477-487) - against the four duplicated cells it replaces, with 474-475 naming what was gained.