writing_good_code#
Series: lecture-python-programming
File:
lectures/writing_good_code.mdAudit date: 2026-08-26
Corpus snapshot:
ceec881028Categories 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 |
|
Math |
9.5/10 |
|
Code |
7/10 |
|
JAX |
out of scope |
JAX rules target |
Figures |
7.5/10 |
|
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.βisself.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 plotcomment 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 sixf'...{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 wrapfortargets 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 literal3that 158 singled out by name (“numbers like50and49and3scattered 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 ofset_one,set_two,set_three,figandaxes, 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 onnpandpltimported 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 buildsp_grid, 393-394 computeqsandqdfrom it, and 397-398 then plot them againstgridfrom the earlier cell at 355, leavingp_gridunused; it renders correctly only because both arenp.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 float0.1at 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
50and49withtime_series_lengthandtime_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
50and49and3”, 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, andtime_series_length=50(253) turns the two magic numbers50and49into one named default andtime_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_pathhelper 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 takesaxas 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.producttrick and must reach for a function or a class.The solution’s
Equilibriumclass 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.25and re-run (477-487) - against the four duplicated cells it replaces, with 474-475 naming what was gained.
Recommended actions#
Fix the two overshooting claims at 284-289, or fix the code they describe:
plt.subplots(3, 1, ...)at 270 still carries the magic3that 158 complained about, coupled to the three parameter sets at 273-275, and nothing in the good version addresses the globals or JIT precepts.Remove the duplication inside the DRY example: line 438 recomputes
np.exp(self.α * p_star) - self.βwhereself.qs(p_star)is defined at 428-429, andcompute_equilibriumshould returnp_star, q_starrather than only print them (440-441).Answer the question at 236: “Which One, Functions or Classes?” is followed by four sentences that decline to choose. Even one sentence of criterion - state held across many calls, or one transformation - would make the heading honest.
Show at least the magic-number and DRY fixes as two-line before-and-after snippets in 156-194; the lecture currently argues about code entirely in prose, with the “after” arriving 100 lines later inside a 30-line cell.
Fix the plotting bug in the exercise code at 388-405:
p_grid(390) feedsqsandqd(393-394) and then 397-398 plot againstgridfrom the earlier cell (355). It renders correctly by coincidence. Either repair it, or - if it is meant as part of the code to be improved - say so in the exercise text at 407-411, which currently mentions only repetition.Do NOT reformat the poor example at 73-134 for PEP8: it is deliberately structured badly and deliberately formatted well, and 136 says so. The four drafted qe-fig-008 hits (91, 108, 125, 263) are genuine
'o-'line plots and addinglw=2is safe, and the twofigsize=(8, 16)overrides (80, 270) have a real reason - three stacked panels - unlike most qe-fig-001 hits, so treat those two as a judgement rather than a defect.Lower-case the nine Title Case headings (45, 142, 156, 176, 196, 215, 226, 236, 246), keeping the acronym in
#### JIT compilation- this is the whole of the drafted Writing score of 4.5 and it is a five-minute fix.Move the three bolds to italic (150, 188) and bold the four terms that are currently defined without it: “magic numbers” (160), Don’t repeat yourself / DRY (182), global variables (198) and WET (230) - and define WET at or before its first use at 228.
Rename the exercise’s parameters so they do not collide with the Solow example: \(\alpha\) and \(\delta\) carry two meanings each (54 and 61 versus 304 and 310), and both live in the same notebook namespace.
Tidy the small code items: the leading space in the six
{p_star: .2f}format specs (347, 348, 384, 385, 440, 441), the leftover# Now plotcomment at 444, the redundantfor-target parentheses at 259 and 277, and the blank-line counts at 253 and 270.Sweep the mechanical remainder: the three two-sentence paragraphs at 27, 319 and 327, the two double-space runs at 148 and 207,
mystnbfigure metadata for the four un-named figure cells (73, 250, 353, 388), the missing full stop at 242, and the two single-item bullet lists at 184 and 230.Consider suppressing or shrinking the second figure at 281: it is identical to the one at 133, and printing the same three-panel 8x16 plot twice costs two pages in a lecture whose subject is the code.